- .NET 指南
- 开始操作
- .NET 教程
- .NET 体系结构组件
- .NET Standard
- 什么是.NET 标准中的新增功能
- 目标框架
- .NET 术语表
- 使用 mdoc 生成文档
- .NET 的体系结构指南
- 使用 ASP.NET Core 和 Azure 构建新式 Web 应用程序
- 现代 Web 应用程序的特征
- 传统 Web 应用程序和单页面应用程序 (Spa) 之间进行选择
- 常见的客户端 Web 技术
- 开发 ASP.NET Core MVC 应用程序
- 使用 ASP.NET Core 应用中的数据
- 测试 ASP.NET Core MVC 应用程序
- Azure 的开发过程
- Azure 托管的 ASP.NET 核心 Web 应用的建议
- 使用 Azure 云和 Windows 容器更新现有 .NET 应用程序 (v1.0)
- 提升和移动现有应用 Azure IaaS
- 将关系数据库迁移到 azure
- 直接迁移现有应用 DevOps
- 到提起并移动现有的.NET 应用到云 DevOps 通用应用程序的原因
- 云 devops 通用应用程序中的 Microsoft 技术
- 云优化应用程序如何呢?
- 如何将现有的.NET 应用程序部署到 Azure App Service
- 将现有的.NET 应用程序部署为 Windows 容器
- 何时不将部署到 Windows 容器
- 何时在你的本地部署 Windows 容器 IaaS VM 基础结构
- 何时将 Windows 容器部署到 Azure Vm (IaaS 云)
- 何时到 Service Fabric 中部署 Windows 容器
- 何时将 Windows 容器部署到 Azure 容器服务 (即,Kubernetes)
- 构建可复原的服务供云: 采用在云中的暂时性故障
- 更新你的应用的监视和遥测
- 更新使用 CI/CD 管道和 DevOps 工具在云中的应用程序的生命周期
- 将迁移到混合云方案
- 演练和技术获取启动的概述
- 技术演练列表
- 技术演练列表
- 结论
- 容器和 Docker 简介
- 什么是 Docker?
- Docker 术语
- Docker 容器、 图像和注册表
- Docker 应用程序生命周期的介绍
- DevOps 协作基础的容器
- 有关适用于容器化应用的 Microsoft 平台和工具的简介
- 使用 Docker 和 Microsoft Azure 设计和开发容器化应用
- Docker 应用程序设计
- 常见容器设计原则
- 整体应用程序
- 状态和 Docker 应用程序中的数据
- SOA 应用程序
- 协调微服务和以高可伸缩性和可用性的 multicontainer 应用程序
- Docker 应用的开发环境
- 对于 Docker 应用程序的内部循环开发工作流
- 使用 Visual Studio Tools for Docker (在 Windows 上的 Visual Studio)
- 使用在 DockerFile 中的 Windows PowerShell 命令来设置 Windows 容器 (Docker 标准基于)
- 使用 Microsoft 工具的 Docker 应用程序 DevOps 工作流
- Docker 应用程序的外部循环 DevOps 工作流中的步骤
- 运行、管理和监视 Docker 生产环境
- 在生产环境中运行由和基于微服务的应用程序
- 管理生产 Docker 环境
- 监视容器化应用程序服务
- 关键结论
- .NET 微服务。 适用于容器化 .NET 应用程序的体系结构
- .NET 微服务。 适用于容器化 .NET 应用程序的体系结构
- 容器和 Docker 简介
- 什么是 Docker?
- Docker 术语
- Docker 容器、 图像和注册表
- 为 Docker 容器选择 .NET Core 还是 .NET Framework
- 一般性指导原则
- 何时选择 Docker 容器中的.NET 核心
- 何时选择 Docker 容器中的.NET Framework
- 决策表:.NET 框架,用于 Docker
- .NET 容器定向到何种操作系统
- 正式.NET Docker 映像
- 构建基于容器和微服务的应用程序
- Containerizing 整体应用程序
- 状态和 Docker 应用程序中的数据
- 面向服务的体系结构
- 微服务体系结构
- 每个微服务构成的数据自主性
- 与物理体系结构的逻辑体系结构
- 分布式的数据管理挑战和解决方案
- 标识每个微服务的域模型边界
- 直接与 API 网关模式的微服务构成客户端通信
- 在微服务体系结构的通信
- 基于消息的异步通信
- 创建、 不断发展,和版本控制 microservice Api 和协定
- 微服务可寻址性和服务注册表
- 创建复合 UI 基于微服务,包括 visual UI 形状和布局由多个微服务生成
- 复原和微服务中的高可用性
- 协调微服务和多容器应用程序的高可伸缩性和可用性
- 使用 Azure Service Fabric
- 基于 Docker 的应用程序的开发流程
- Docker 应用的开发工作流
- 在 Linux 或 Windows Nano Server 主机上部署基于单容器的 .NET Core Web 应用
- 将旧版整体式 .NET Framework 应用程序迁移到 Windows 容器
- 设计和开发基于微服务的多容器 .NET 应用程序
- 设计面向微服务构成的应用程序
- 创建简单的数据驱动 CRUD 微服务
- 定义与 docker-compose.yml 多容器应用程序
- 使用运行的容器的数据库服务器
- 实现基于事件的微服务 (集成事件) 之间的通信
- 实现使用 RabbitMQ 开发或测试环境事件总线
- 订阅事件
- 测试 ASP.NET 核心服务和 web 应用
- 使用 DDD 和 CQRS 模式降低微服务中的业务复杂性
- 应用中的微服务构成的简化的 CQRS 和 DDD 模式
- EShopOnContainers DDD 微服务中的应用 CQRS 和 CQS 方法
- 在 CQRS 微服务中实现读取/查询
- 设计 DDD 面向微服务
- 设计 microservice 域模型
- 实现 microservice 域模型与.NET 核心
- Seedwork (可重用基类,这些类和接口,您的域模型)
- 实现值对象
- 使用枚举类,而不枚举类型
- 设计域模型层中的验证
- 客户端验证 (验证在表示层)
- 域事件: 设计和实现
- 设计基础结构持久性层
- 实现与实体框架核心基础结构持久性层
- 作为持久性基础结构使用 NoSQL 数据库
- 设计微服务应用程序层和 Web API
- 实现微服务应用程序层使用 Web API
- 实现具有恢复能力的应用程序
- 处理部分失败
- 用于处理部分失败的策略
- 实现重试使用指数退让
- 实现弹性 Entity Framework 核心 SQL 连接
- 实现自定义 HTTP 调用的重试使用指数退让
- 使用 Polly 和实施 HTTP 调用重试使用指数退让
- 实现断路器模式
- 运行状况监视
- 保护 .NET 微服务和 Web 应用程序
- 有关在.NET 微服务和 web 应用程序的授权
- 在开发过程中安全地存储应用程序机密
- 使用 Azure 密钥保管库在生产时保护机密
- 记住的要点
- 为服务器应用选择 .NET Core 或 .NET Framework
- 什么是 托管代码 ?
- Automatic Memory Management
- 公共语言运行时 (CLR)
- 语言独立性和与语言无关的组件
- 语言独立性和与语言无关的组件
- 框架库
- .NET Framework 类库概述
- 在 .NET 中使用基类型
- 常规类型系统
- .NET Framework 中的类型转换
- .NET 中的类型转换表
- .NET 中的格式化类型
- 标准数字格式字符串
- 自定义数字格式字符串
- 标准日期和时间格式字符串
- 自定义日期和时间格式字符串
- 标准 TimeSpan 格式字符串
- 自定义的 TimeSpan 格式字符串
- 枚举格式字符串
- 复合格式设置
- 执行格式设置操作
- 如何:用前导零填充数字
- 如何:从特定日期中提取星期几
- 如何:定义和使用自定义数值格式提供程序
- 如何:往返日期和时间值
- 如何:将用户在 Web 控件中输入的数值转换为数字
- 如何:向 Web 用户显示本地化的日期和时间信息
- 如何:显示日期和时间值中的毫秒
- 如何:用非公历日历显示日期
- 操作在.NET 中的字符串
- 在.NET 中使用字符串的最佳做法
- .NET 中的基本字符串操作
- 在.NET 中创建新的字符串
- 剪裁和移除从.NET 中的字符串的字符
- .NET 中的空白字符串
- 比较.NET 中的字符串
- 更改.NET 中的大小写
- 在.NET 中使用 StringBuilder 类
- 如何: 在.NET 中执行基本字符串操作
- .NET 正则表达式
- 正则表达式语言 - 快速参考
- 正则表达式中的字符转义
- 正则表达式中的字符类
- 正则表达式中的定位点
- 正则表达式中的分组构造
- 正则表达式中的限定符
- 正则表达式中的反向引用构造
- 正则表达式中的备用构造
- 正则表达式中的替代
- 正则表达式选项
- 正则表达式中的其他构造
- .NET 中的正则表达式的最佳实践
- 正则表达式对象模型
- 正则表达式行为的详细信息
- 正则表达式中的回溯
- 正则表达式中的编译和重复使用
- 正则表达式中的线程安全
- 正则表达式示例
- 正则表达式示例:扫描 HREF
- 正则表达式示例:更改日期格式
- 如何:从 URL 中提取协议和端口号
- 如何:从字符串中剥离无效字符
- 如何:确认字符串是有效的电子邮件格式
- 在.NET 中编码的字符
- 在.NET 中分析字符串
- NET 中分析数值字符串
- 分析日期和时间字符串.NET 中
- .NET 中分析其他字符串
- .NET 类库
- .NET 可移植性分析器
- 在 .NET 中处理和引发异常
- .NET 程序集文件格式
- 内存管理和.NET 中的垃圾回收
- 垃圾回收
- 垃圾回收的基础
- 垃圾回收和性能
- 被动回收
- 滞后时间模式
- 针对共享 Web 承载优化
- 垃圾回收通知
- 应用程序域资源监控
- 弱引用
- 泛型类型(泛型)概述
- 委托和 lambda
- LINQ(语言集成查询)
- 常规类型系统和公共语言规范
- 异步概述
- 深入了解异步
- 异步编程模式
- 基于任务的异步模式 (TAP)
- 实现基于任务的异步模式
- 使用基于任务的异步模式
- 与其他异步模式和类型互操作
- 基于事件的异步模式 (EAP)
- 使用基于事件的异步模式进行多线程编程
- 基于事件的异步模式概述
- 实现基于事件的异步模式
- 实现基于事件的异步模式的最佳做法
- 确定何时实现基于事件的异步模式
- 演练:实现支持基于事件的异步模式的组件
- 演练:实现支持基于事件的异步模式的组件
- 如何:实现基于事件的异步模式的客户端
- 如何:使用支持基于事件的异步模式的组件
- 异步编程模型 (APM)
- 使用 IAsyncResult 调用异步方法
- 使用 AsyncWaitHandle 阻止应用程序的执行
- 通过结束异步操作来阻止应用程序执行
- 轮询异步操作的状态
- 使用 AsyncCallback 委托结束异步操作
- 使用 AsyncCallback 委托和状态对象
- 使用委托进行异步编程
- 使用异步方式调用同步方法
- 本机互操作性
- 集合和数据结构
- .NET Framework 中的数字
- 日期、时间和时区
- 时区概述
- 在 DateTime、DateTimeOffset、TimeSpan 和 TimeZoneInfo 之间进行选择
- 查找本地系统上定义的时区
- 如何: 枚举计算机上存在的时区
- 如何: 访问预定义的 UTC 和当地时间区域对象
- 如何: 实例化 TimeZoneInfo 对象
- 实例化 DateTimeOffset 对象
- 如何: 创建不带调整规则的时区
- 如何: 创建带有调整规则的时区
- 保存和还原时区
- 如何: 将时区保存到嵌入的资源
- 如何: 从嵌入的资源还原时区
- 使用日历
- 使用日期和时间执行算术运算
- 如何: 在日期和时间运算中使用时区
- 在 DateTime 与 DateTimeOffset 之间进行转换
- 在各时区之间转换时间
- 如何: 解决不明确的时间
- 如何: 让用户解决不明确的时间
- 处理和引发事件
- 如何:引发和使用事件
- 如何:使用事件属性处理多个事件
- 如何:在 Web 窗体应用程序中使用事件
- 观察程序设计模式
- 观察程序设计模式最佳做法
- 如何:实现提供程序
- 如何:实现观察程序
- 托管执行过程
- 元数据和自描述组件
- 在 .NET Framework 中构建控制台应用程序
- .NET Framework 中的并行处理和并发
- .NET Framework 应用程序要点
- 文件和流 I/O
- 对 .NET Framework 应用程序进行全球化和本地化
- 全球化
- 本地化评审
- 本地化
- 不区分区域性的字符串操作
- 执行不区分区域性的字符串操作
- 执行不区分区域性的字符串比较
- 执行不区分区域性的大小写更改
- 在集合中执行不区分区域性的字符串操作
- 在数组中执行不区分区域性的字符串操作
- 开发全球通用应用程序的最佳做法
- 利用特性扩展元数据
- 应用特性
- 编写自定义特性
- 检索存储在特性中的信息
- 框架设计准则
- 命名准则
- 大小写约定
- 通用命名约定
- 程序集和 DLL 的名称
- 命名空间的名称
- 类、结构和接口的名称
- 类型成员的名称
- 命名参数
- 命名资源
- 类型设计准则
- 在类和结构之间选择
- 抽象类设计
- 静态类设计
- 接口设计
- 结构设计
- 枚举设计
- 嵌套类型
- 成员设计准则
- 成员重载
- 属性设计
- 构造函数设计
- 事件设计
- 字段设计
- 扩展方法
- 运算符重载
- 参数设计
- 扩展性设计
- 未密封类
- 受保护的成员
- 事件和回调
- 虚成员
- 抽象(抽象类型和接口)
- 用于实现抽象的基类
- 密封
- 异常设计准则
- 异常引发
- 使用标准异常类型
- 异常和性能
- 使用准则
- 数组
- 特性
- 集合准则
- 序列化
- System.Xml 使用情况
- 相等运算符
- 常见设计模式
- 依赖项属性
- 释放模式
- XML 文档和数据
- XML 处理选项
- 内存中 XML 数据处理
- 使用 DOM 模型处理 XML 数据
- XML 文档对象模型 (DOM)
- XML 节点类型
- XML 文档对象模型 (DOM) 层次结构
- 将对象层次结构映射到 XML 数据
- 创建 XML 文档
- 将 XML 文档读入 DOM
- 嵌入到文档中的样式表指令
- 从读取器中加载数据
- 加载 DOM 时的空白和有效空白处理
- 访问 DOM 中的属性
- 将实体声明和实体引用读入 DOM
- 保留实体引用
- 扩展但不保留实体引用
- 将节点插入 XML 文档中
- 在 DOM 中创建新节点
- 为 DOM 中的元素创建新属性
- 创建新节点时的 XML 元素和属性名验证
- 创建新实体引用
- 命名空间对包含元素和属性的新节点的实体引用扩展的影响
- 复制现有节点
- 将现有节点从一个文档复制到另一个文档
- 复制文档片段
- 移除 XML 文档中的节点、内容和值
- 从 DOM 中移除节点
- 移除 DOM 中元素节点的属性
- 移除 DOM 中的节点内容
- 修改 XML 文档中的节点、内容和值
- 在 DOM 中验证 XML 文档
- 保存和写出文档
- 使用 XPath 导航选择节点
- 解析外部资源
- 使用 XmlNameTable 的对象比较
- NamedNodeMap 和 NodeList 中的节点集合
- 按名称或索引检索未排序节点
- 按索引检索已排序节点
- NodeList 和 NamedNodeMap 的动态更新
- DOM 中的命名空间支持
- DOM 中的命名空间和 DTD
- 更改 XML 文档中的命名空间声明
- 更改命名空间前缀属性
- 使用 mlNodeChangedEventArgs 的 XML 文档中的事件处理
- 扩展 DOM
- 使用 XPath 数据模型处理 XML 数据
- 使用 XPathDocument 和 XmlDocument 读取 XML 数据
- 使用 XPathNavigator 选择、计算和匹配 XML 数据
- 使用 XPathNavigator 选择 XML 数据
- 使用 XPathNavigator 计算 XPath 表达式
- 使用 XPathNavigator 匹配节点
- XPath 查询识别的节点类型
- XPath 查询和命名空间
- 已编译的 XPath 表达式
- XPath 命名空间浏览
- 使用 XPathNavigator 访问 XML 数据
- 使用 XPathNavigator 的节点集定位
- 使用 XPathNavigator 的属性和命名空间节点定位
- 使用 XPathNavigator 提取 XML 数据
- 使用 XPathNavigator 访问强类型 XML 数据
- 用户定义的函数和变量
- 使用 XPathNavigator 编辑 XML 数据
- 使用 XPathNavigator 插入 XML 数据
- 使用 XPathNavigator 修改 XML 数据
- 使用 XPathNavigator 移除 XML 数据
- 使用 XPathNavigator 验证架构
- 使用 LINQ to XML 处理 XML 数据
- XSLT 转换
- 使用 XslCompiledTransform 类
- XslCompiledTransform 类的输入
- XslCompiledTransform 类的输出选项
- 在 XSLT 处理期间解析外部资源
- 扩展 XSLT 样式表
- XSLT 扩展对象
- XSLT 参数
- 使用 msxsl:script 的脚本块
- 可恢复的 XSLT 错误
- 如何:转换节点片断
- 从 XslTransform 类迁移
- 如何:迁移 XslTransform 代码
- XSLT 安全注意事项
- XSLT 编译器 (xsltc.exe)
- 如何:通过使用程序集执行 XSLT 转换
- XslTransform 类的 XSLT 转换
- XslTransform 类中任意行为的实现
- XslTransform 类实现 XSLT 处理器
- XslTransform 的输出
- 不同存储区的 XSLT 转换
- 解析外部 XSLT 样式表和文档
- 样式表参数和扩展对象的 XsltArgumentList
- XSLT 样式表脚本使用<msxsl: script>
- 对 msxsl:node-set() 函数的支持
- 转换中的节点集
- 转换中的结果树片断
- 转换中的 XPathNavigator
- 转换中的 XPathNodeIterator
- XslTransform 的 XPathDocument 输入
- XslTransform 的 XmlDataDocument 输入
- XslTransform 的 XmlDocument 输入
- 使用 XML 架构
- XML 架构对象模型 (SOM)
- XML 架构对象模型概述
- 读写 XML 架构
- 生成 XML 架构
- 遍历 XML 架构
- 编辑 XML 架构
- 包含或导入 XML 架构
- 用于编译架构的 XmlSchemaSet
- 后架构编译信息集
- 使用 XmlSchemaSet 进行 XML 架构 (XSD) 验证
- XmlSchemaCollection 架构编译
- 使用 XmlSchemaCollection 进行 XDR 验证
- 使用 XmlSchemaCollection 进行 XML 架构 (XSD) 验证
- XmlSchemaValidator 基于推送的验证
- 推断 XML 架构
- 从 XML 文档推断架构
- 推断架构节点类型和结构的规则
- 推断简单类型的规则
- 关系数据和 ADO.NET 的 XML 集成
- 管理 XML 文档中的命名空间
- System.Xml 类中的类型支持
- 将 XML 数据类型映射到 CLR 类型
- XML 类型支持实现说明
- XML 数据类型的转换
- 将字符串转换为 .NET Framework 数据类型
- 将 .NET Framework 类型转换为字符串
- 托管线程处理
- 托管线程处理基本知识
- 线程与线程处理
- 托管线程中的异常
- 为多线程处理同步数据
- 托管线程状态
- 前台和后台线程
- Windows 中的托管和非托管线程处理
- Thread.Suspend、垃圾回收和安全点
- 线程本地存储区:线程相关的静态字段和数据槽
- 托管线程中的取消
- 如何:通过轮询侦听取消请求
- 如何:注册取消请求的回调
- 如何:侦听具有等待句柄的取消请求
- 如何:侦听多个取消请求
- 使用线程和线程处理
- 启动时创建线程并传递数据
- 暂停和继续线程
- 销毁线程
- 调度线程
- 以协作方式取消线程
- 托管线程处理的最佳做法
- 线程处理对象和功能
- 托管线程池
- 计时器
- EventWaitHandle、AutoResetEvent、CountdownEvent、ManualResetEvent
- EventWaitHandle
- AutoResetEvent
- ManualResetEvent 和 ManualResetEventSlim
- CountdownEvent
- Mutexes
- 互锁操作
- 读取器/编写器锁
- Semaphore 和 SemaphoreSlim
- 同步基元概述
- 屏障 (.NET Framework)
- 如何:使用屏障来使并发操作保持同步
- SpinLock
- 如何:使用 SpinLock 进行低级别同步
- 如何:在 SpinLock 中启用线程跟踪模式
- SpinWait
- 如何:使用 SpinWait 实现两阶段等待操作
- .NET 中的并行编程
- 任务并行库 (TPL)
- 数据并行(任务并行库)
- 如何:编写简单的 Parallel.For 循环
- 如何:编写简单的 Parallel.ForEach 循环
- 如何:编写具有线程局部变量的 Parallel.For 循环
- 如何:编写具有线程局部变量的 Parallel.ForEach 循环
- 如何:取消 Parallel.For 或 ForEach Loop
- 如何:处理并行循环中的异常
- 如何:加快小型循环体的速度
- 如何:使用并行类循环访问文件目录
- 基于任务的异步编程
- 使用延续任务来链接任务
- 已附加和已分离的子任务
- 任务取消
- 异常处理(任务并行库)
- 如何:使用 Parallel.Invoke 来执行并行操作
- 如何:从任务中返回值
- 如何:取消任务及其子级
- 如何:创建预先计算的任务
- 如何:使用并行任务遍历二叉树
- 如何:解除嵌套任务的包装
- 如何:防止子任务附加到其父任务
- 数据流(任务并行库)
- 如何:将消息写入数据流块和从数据流块读取消息
- 如何:实现制造者-使用者数据流模式
- 如何:在数据流块收到数据时执行操作
- 演练:创建数据流管道
- 如何:取消链接数据流块
- 演练:在 Windows 窗体应用程序中使用数据流
- 如何:取消数据流块
- 演练:创建自定义数据流块类型
- 如何:使用 JoinBlock 从多个源读取数据
- 如何:指定数据流块中的并行度
- 如何:在数据流块中指定任务计划程序
- 演练:使用 BatchBlock 和 BatchedJoinBlock 提高效率
- 将 TPL 用于其他异步模式
- TPL 和传统 .NET Framework 异步编程
- 如何:在任务中包装 EAP 模式
- 数据并行和任务并行中的潜在缺陷
- 并行 LINQ (PLINQ)
- PLINQ 介绍
- 了解 PLINQ 中的加速
- PLINQ 中的顺序保留
- PLINQ 中的合并选项
- PLINQ 的潜在缺陷
- 如何:创建并执行简单的 PLINQ 查询
- 如何:在 PLINQ 查询中控制排序
- 如何:合并并行和顺序 LINQ 查询
- 如何:处理 PLINQ 查询中的异常
- 如何:取消 PLINQ 查询
- 如何:编写自定义 PLINQ 聚合函数
- 如何:在 PLINQ 中指定执行模式
- 如何:在 PLINQ 中指定合并选项
- 如何:使用 PLINQ 循环访问文件目录
- 如何:衡量 PLINQ 查询性能
- PLINQ 数据示例
- 用于并行编程的数据结构
- 并行诊断工具
- PLINQ 和 TPL 的自定义分区程序
- 如何:实现动态分区
- 如何:实现静态分区的分区程序
- PLINQ 和 TPL 中的 Lambda 表达式
- 其他阅读材料(并行编程)
- .NET Framework 中的安全性
- 安全性的基础概念
- 基于角色的安全性
- 主体和标识对象
- 如何:创建 WindowsPrincipal 对象
- 如何:创建 GenericPrincipal 和 GenericIdentity 对象
- 替换 Principal 对象
- 模拟与恢复
- .NET Framework 加密模型
- 加密服务
- 生成加密和解密的密钥
- 如何:将非对称密钥存储在密钥容器中
- 加密数据
- 解密数据
- 加密签名
- 使用哈希代码确保数据完整性
- 创建加密方案
- 如何:用对称密钥对 XML 元素进行加密
- 如何:用对称密钥对 XML 元素进行解密
- 如何:用非对称密钥对 XML 元素进行加密
- 如何:用非对称密钥对 XML 元素进行解密
- 如何:用 X.509 证书对 XML 元素进行加密
- 如何:用 X.509 证书对 XML 元素进行解密
- 如何:使用数字签名为 XML 文档签名
- 如何:验证 XML 文档的数字签名
- 如何:使用数据保护
- 如何:访问硬件加密设备
- 演练:创建加密应用程序
- 代码安全维护指南
- 保护状态数据
- 安全性和用户输入
- 安全和争用条件
- 安全性和进行中的代码生成
- .NET 中的序列化
- 序列化帮助主题
- 二进制序列化
- 序列化概念
- 基本序列化
- 有选择的序列化
- 自定义序列化
- 序列化过程中的步骤
- 版本容错序列化
- 序列化准则
- 如何:对序列化数据进行分块
- 如何: 确定是否可序列化的标准.NET 对象
- 请参阅
- 请参阅
- XML 和 SOAP 序列化
- 如何:控制派生类的序列化
- XML 序列化简介
- 如何:反序列化对象
- XML 序列化示例
- XML 架构定义工具和 XML 序列化
- How to: Use the XML Schema Definition Tool to Generate Classes and XML Schema Documents
- 使用属性控制 XML 序列化
- 用来控制 XML 序列化的属性
- 如何:指定 XML 流的替代元素名称
- 如何:序列化对象
- 如何:限定 XML 元素和 XML 属性名
- 使用 XML Web services 进行 XML 序列化
- 如何:将对象序列化为 SOAP 编码的 XML 流
- 如何:重写编码的 SOAP XML 序列化
- 用来控制编码的 SOAP 序列化的属性
- <system.xml.serialization> 元素
- <dateTimeSerialization> 元素
- <schemaImporterExtensions> 元素
- <xmlSerializer> 元素
- 序列化工具
- XML 序列化程序生成器工具 (Sgen.exe)
- XML Schema Definition Tool (Xsd.exe)
- .NET Framework 的序列化示例
- 基本序列化技术示例
- 使用 XmlSerializer 自定义序列化顺序
- SchemaImporterExtension 技术示例
- 版本容错序列化技术示例
- Web 服务泛型序列化技术示例
- Web 服务 IXmlSerializable 技术示例
- 使用 .NET Framework 针对多个平台开发
- 使用可移植类库的跨平台开发
- 将可移植类库与模型-视图-视图模型配合使用
- 面向多个平台的库的应用程序资源
- .NET Framework 对 Windows 应用商店应用程序和 Windows 运行时的支持情况
- 向 Windows 运行时传递 URI
- WindowsRuntimeStreamExtensions.AsRandomAccessStream(System.IO.Stream) 方法
.NET 中的正则表达式的最佳实践
.NET 中的正则表达式引擎是一个功能强大、 功能完备的工具,它基于模式匹配中,而不是比较和匹配文本来处理。 在大多数情况下,它可以快速、高效地执行模式匹配。 但在某些情况下,正则表达式引擎的速度似乎很慢。 在极端情况下,它甚至看似停止响应,因为它会用若干个小时甚至若干天处理相对小的输入。
本主题概述开发人员为了确保其正则表达式实现最佳性能可以采纳的一些最佳做法。 它包含下列部分:
考虑输入源
通常,正则表达式可接受两种类型的输入:受约束的输入或不受约束的输入。 受约束的输入是源自已知或可靠的源并遵循预定义格式的文本。 不受约束的输入是源自不可靠的源(如 Web 用户)并且可能不遵循预定义或预期格式的文本。
编写的正则表达式模式的目的通常是匹配有效输入。 也就是说,开发人员检查他们要匹配的文本,然后编写与其匹配的正则表达式模式。 然后,开发人员使用多个有效输入项进行测试,以确定此模式是否需要更正或进一步细化。 当模式可匹配所有假定的有效输入时,则将其声明为生产就绪并且可包括在发布的应用程序中。 这使得正则表达式模式适合匹配受约束的输入。 但它不适合匹配不受约束的输入。
若要匹配不受约束的输入,正则表达式必须能够高效处理以下三种文本:
- 与正则表达式模式匹配的文本。
- 与正则表达式模式不匹配的文本。
- 与正则表达式模式大致匹配的文本。
对于为了处理受约束的输入而编写的正则表达式,最后一种文本类型尤其存在问题。 如果该正则表达式还依赖大量 回溯 ,则正则表达式引擎可能会花费大量时间(在有些情况下,需要许多个小时或许多天)来处理看似无害的文本。
警告
下面的示例使用容易过度回溯并可能拒绝有效电子邮件地址的正则表达式。 不应在电子邮件验证例程中使用。 如果要验证电子邮件地址的正则表达式,请参阅 如何:确认字符串是有效的电子邮件格式 。
例如,考虑一种很常用但很有问题的用于验证电子邮件地址别名的正则表达式。 编写正则表达式 ^[0-9A-Z]([-.\w]*[0-9A-Z])*$
的目的是处理被视为有效的电子邮件地址,该地址包含一个字母数字字符,后跟零个或多个可为字母数字、句点或连字符的字符。 该正则表达式必须以字母数字字符结束。 但正如下面的示例所示,尽管此正则表达式可以轻松处理有效输入,但在处理接近有效的输入时性能非常低效。
using System;
using System.Diagnostics;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
Stopwatch sw;
string[] addresses = { "AAAAAAAAAAA@contoso.com",
"AAAAAAAAAAaaaaaaaaaa!@contoso.com" };
// The following regular expression should not actually be used to
// validate an email address.
string pattern = @"^[0-9A-Z]([-.\w]*[0-9A-Z])*$";
string input;
foreach (var address in addresses) {
string mailBox = address.Substring(0, address.IndexOf("@"));
int index = 0;
for (int ctr = mailBox.Length - 1; ctr >= 0; ctr--) {
index++;
input = mailBox.Substring(ctr, index);
sw = Stopwatch.StartNew();
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
sw.Stop();
if (m.Success)
Console.WriteLine("{0,2}. Matched '{1,25}' in {2}",
index, m.Value, sw.Elapsed);
else
Console.WriteLine("{0,2}. Failed '{1,25}' in {2}",
index, input, sw.Elapsed);
}
Console.WriteLine();
}
}
}
// The example displays output similar to the following:
// 1. Matched ' A' in 00:00:00.0007122
// 2. Matched ' AA' in 00:00:00.0000282
// 3. Matched ' AAA' in 00:00:00.0000042
// 4. Matched ' AAAA' in 00:00:00.0000038
// 5. Matched ' AAAAA' in 00:00:00.0000042
// 6. Matched ' AAAAAA' in 00:00:00.0000042
// 7. Matched ' AAAAAAA' in 00:00:00.0000042
// 8. Matched ' AAAAAAAA' in 00:00:00.0000087
// 9. Matched ' AAAAAAAAA' in 00:00:00.0000045
// 10. Matched ' AAAAAAAAAA' in 00:00:00.0000045
// 11. Matched ' AAAAAAAAAAA' in 00:00:00.0000045
//
// 1. Failed ' !' in 00:00:00.0000447
// 2. Failed ' a!' in 00:00:00.0000071
// 3. Failed ' aa!' in 00:00:00.0000071
// 4. Failed ' aaa!' in 00:00:00.0000061
// 5. Failed ' aaaa!' in 00:00:00.0000081
// 6. Failed ' aaaaa!' in 00:00:00.0000126
// 7. Failed ' aaaaaa!' in 00:00:00.0000359
// 8. Failed ' aaaaaaa!' in 00:00:00.0000414
// 9. Failed ' aaaaaaaa!' in 00:00:00.0000758
// 10. Failed ' aaaaaaaaa!' in 00:00:00.0001462
// 11. Failed ' aaaaaaaaaa!' in 00:00:00.0002885
// 12. Failed ' Aaaaaaaaaaa!' in 00:00:00.0005780
// 13. Failed ' AAaaaaaaaaaa!' in 00:00:00.0011628
// 14. Failed ' AAAaaaaaaaaaa!' in 00:00:00.0022851
// 15. Failed ' AAAAaaaaaaaaaa!' in 00:00:00.0045864
// 16. Failed ' AAAAAaaaaaaaaaa!' in 00:00:00.0093168
// 17. Failed ' AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
// 18. Failed ' AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
// 19. Failed ' AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
// 20. Failed ' AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
// 21. Failed ' AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim sw As Stopwatch
Dim addresses() As String = { "AAAAAAAAAAA@contoso.com",
"AAAAAAAAAAaaaaaaaaaa!@contoso.com" }
' The following regular expression should not actually be used to
' validate an email address.
Dim pattern As String = "^[0-9A-Z]([-.\w]*[0-9A-Z])*$"
Dim input As String
For Each address In addresses
Dim mailBox As String = address.Substring(0, address.IndexOf("@"))
Dim index As Integer = 0
For ctr As Integer = mailBox.Length - 1 To 0 Step -1
index += 1
input = mailBox.Substring(ctr, index)
sw = Stopwatch.StartNew()
Dim m As Match = Regex.Match(input, pattern, RegexOptions.IgnoreCase)
sw.Stop()
if m.Success Then
Console.WriteLine("{0,2}. Matched '{1,25}' in {2}",
index, m.Value, sw.Elapsed)
Else
Console.WriteLine("{0,2}. Failed '{1,25}' in {2}",
index, input, sw.Elapsed)
End If
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays output similar to the following:
' 1. Matched ' A' in 00:00:00.0007122
' 2. Matched ' AA' in 00:00:00.0000282
' 3. Matched ' AAA' in 00:00:00.0000042
' 4. Matched ' AAAA' in 00:00:00.0000038
' 5. Matched ' AAAAA' in 00:00:00.0000042
' 6. Matched ' AAAAAA' in 00:00:00.0000042
' 7. Matched ' AAAAAAA' in 00:00:00.0000042
' 8. Matched ' AAAAAAAA' in 00:00:00.0000087
' 9. Matched ' AAAAAAAAA' in 00:00:00.0000045
' 10. Matched ' AAAAAAAAAA' in 00:00:00.0000045
' 11. Matched ' AAAAAAAAAAA' in 00:00:00.0000045
'
' 1. Failed ' !' in 00:00:00.0000447
' 2. Failed ' a!' in 00:00:00.0000071
' 3. Failed ' aa!' in 00:00:00.0000071
' 4. Failed ' aaa!' in 00:00:00.0000061
' 5. Failed ' aaaa!' in 00:00:00.0000081
' 6. Failed ' aaaaa!' in 00:00:00.0000126
' 7. Failed ' aaaaaa!' in 00:00:00.0000359
' 8. Failed ' aaaaaaa!' in 00:00:00.0000414
' 9. Failed ' aaaaaaaa!' in 00:00:00.0000758
' 10. Failed ' aaaaaaaaa!' in 00:00:00.0001462
' 11. Failed ' aaaaaaaaaa!' in 00:00:00.0002885
' 12. Failed ' Aaaaaaaaaaa!' in 00:00:00.0005780
' 13. Failed ' AAaaaaaaaaaa!' in 00:00:00.0011628
' 14. Failed ' AAAaaaaaaaaaa!' in 00:00:00.0022851
' 15. Failed ' AAAAaaaaaaaaaa!' in 00:00:00.0045864
' 16. Failed ' AAAAAaaaaaaaaaa!' in 00:00:00.0093168
' 17. Failed ' AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
' 18. Failed ' AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
' 19. Failed ' AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
' 20. Failed ' AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
' 21. Failed ' AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372
如该示例输出所示,正则表达式引擎处理有效电子邮件别名的时间间隔大致相同,与其长度无关。 另一方面,当接近有效的电子邮件地址包含五个以上字符时,字符串中每增加一个字符,处理时间会大约增加一倍。 这意味着,处理接近有效的 28 个字符构成的字符串将需要一个小时,处理接近有效的 33 个字符构成的字符串将需要接近一天的时间。
由于开发此正则表达式时只考虑了要匹配的输入的格式,因此未能考虑与模式不匹配的输入。 这反过来会使与正则表达式模式近似匹配的不受约束输入的性能显著降低。
若要解决此问题,可执行下列操作:
- 开发模式时,应考虑回溯对正则表达式引擎的性能的影响程度,特别是当正则表达式设计用于处理不受约束的输入时。 有关详细信息,请参阅 控制回溯 部分。
- 使用无效输入、接近有效的输入以及有效输入对正则表达式进行完全测试。 若要为特定正则表达式随机生成输入,可以使用 Rex ,这是 Microsoft Research 提供的正则表达式探索工具。
适当处理对象实例化
核心。NET 的正则表达式对象模型是 System.Text.RegularExpressions.Regex 类,该类表示正则表达式引擎。 通常,影响正则表达式性能的单个最大因素是 Regex 引擎的使用方式。 定义正则表达式需要将正则表达式引擎与正则表达式模式紧密耦合。 无论该耦合过程是需要通过向其构造函数传递正则表达式模式来实例化 Regex 还是通过向其传递正则表达式模式和要分析的字符串来调用静态方法,都必然会消耗大量资源。
备注
使用解释并已编译的正则表达式的性能影响的更多详细讨论,请参阅 优化正则表达式性能,第 ii 部分: 采用控制回溯 BCL 团队博客中。
可将正则表达式引擎与特定正则表达式模式耦合,然后使用该引擎以若干种方式匹配文本:
- 可以调用静态模式匹配方法,如 Regex.Match(String, String) 。 这不需要实例化正则表达式对象。
- 可以实例化一个 Regex 对象并调用已解释的正则表达式的实例模式匹配方法。 这是将正则表达式引擎绑定到正则表达式模式的默认方法。 如果实例化 Regex 对象时未使用包括
options
标记的 Compiled 参数,则会生成此方法。 - 可以实例化一个 Regex 对象并调用已编译的正则表达式的实例模式匹配方法。 当使用包括 Regex 标记的
options
参数实例化 Compiled 对象时,正则表达式对象表示已编译的模式。 - 可以创建一个与特定正则表达式模式紧密耦合的特殊用途的 Regex 对象,编译该对象,并将其保存到独立程序集中。 为此,可调用 Regex.CompileToAssembly 方法。
这种调用正则表达式匹配方法的特殊方式会对应用程序产生显著影响。 以下各节讨论何时使用静态方法调用、已解释的正则表达式和已编译的正则表达式,以改进应用程序的性能。
重要
如果方法调用中重复使用同一正则表达式或者应用程序大量使用正则表达式对象,则方法调用的形式(静态、已解释、已编译)会影响性能。
静态正则表达式
建议将静态正则表达式方法用作使用同一正则表达式重复实例化正则表达式对象的替代方法。 与正则表达式对象使用的正则表达式模式不同,实例方法调用所使用的模式中的操作代码或已编译的 Microsoft 中间语言 (MSIL) 由正则表达式引擎缓存在内部。
例如,事件处理程序会频繁调用其他方法来验证用户输入。 下面的代码中反映了这一点,其中一个 Button 控件的 Click 事件用于调用名为 IsValidCurrency
的方法,该方法检查用户是否输入了后跟至少一个十进制数的货币符号。
public void OKButton_Click(object sender, EventArgs e)
{
if (! String.IsNullOrEmpty(sourceCurrency.Text))
if (RegexLib.IsValidCurrency(sourceCurrency.Text))
PerformConversion();
else
status.Text = "The source currency value is invalid.";
}
Public Sub OKButton_Click(sender As Object, e As EventArgs) _
Handles OKButton.Click
If Not String.IsNullOrEmpty(sourceCurrency.Text) Then
If RegexLib.IsValidCurrency(sourceCurrency.Text) Then
PerformConversion()
Else
status.Text = "The source currency value is invalid."
End If
End If
End Sub
下面的示例显示 IsValidCurrency
方法的一个非常低效的实现。 请注意,每个方法调用使用相同模式重新实例化 Regex 对象。 这反过来意味着,每次调用该方法时,都必须重新编译正则表达式模式。
using System;
using System.Text.RegularExpressions;
public class RegexLib
{
public static bool IsValidCurrency(string currencyValue)
{
string pattern = @"\p{Sc}+\s*\d+";
Regex currencyRegex = new Regex(pattern);
return currencyRegex.IsMatch(currencyValue);
}
}
Imports System.Text.RegularExpressions
Public Module RegexLib
Public Function IsValidCurrency(currencyValue As String) As Boolean
Dim pattern As String = "\p{Sc}+\s*\d+"
Dim currencyRegex As New Regex(pattern)
Return currencyRegex.IsMatch(currencyValue)
End Function
End Module
应将此低效代码替换为对静态 Regex.IsMatch(String, String) 方法的调用。 这样便不必在你每次要调用模式匹配方法时都实例化 Regex 对象,还允许正则表达式引擎从其缓存中检索正则表达式的已编译版本。
using System;
using System.Text.RegularExpressions;
public class RegexLib
{
public static bool IsValidCurrency(string currencyValue)
{
string pattern = @"\p{Sc}+\s*\d+";
return Regex.IsMatch(currencyValue, pattern);
}
}
Imports System.Text.RegularExpressions
Public Module RegexLib
Public Function IsValidCurrency(currencyValue As String) As Boolean
Dim pattern As String = "\p{Sc}+\s*\d+"
Return Regex.IsMatch(currencyValue, pattern)
End Function
End Module
默认情况下,将缓存最后 15 个最近使用的静态正则表达式模式。 对于需要大量已缓存的静态正则表达式的应用程序,可通过设置 Regex.CacheSize 属性来调整缓存大小。
此示例中使用的正则表达式 \p{Sc}+\s*\d+
可验证输入字符串是否包含一个货币符号和至少一个十进制数。 模式的定义如下表所示。
模式 | 描述 |
---|---|
\p{Sc}+ | 与 Unicode 符号、货币类别中的一个或多个字符匹配。 |
\s* | 匹配零个或多个空白字符。 |
\d+ | 匹配一个或多个十进制数字。 |
已解释与已编译的正则表达式
将解释未通过 Compiled 选项的规范绑定到正则表达式引擎的正则表达式模式。 在实例化正则表达式对象时,正则表达式引擎会将正则表达式转换为一组操作代码。 调用实例方法时,操作代码会转换为 MSIL 并由 JIT 编译器执行。 同样,当调用一种静态正则表达式方法并且在缓存中找不到该正则表达式时,正则表达式引擎会将该正则表达式转换为一组操作代码并将其存储在缓存中。 然后,它将这些操作代码转换为 MSIL,以便于 JIT 编译器执行。 已解释的正则表达式会减少启动时间,但会使执行速度变慢。 因此,在少数方法调用中使用正则表达式时或调用正则表达式方法的确切数量未知但预期很小时,使用已解释的正则表达式的效果最佳。 随着方法调用数量的增加,执行速度变慢对性能的影响会超过减少启动时间带来的性能改进。
将编译未通过 Compiled 选项的规范绑定到正则表达式引擎的正则表达式模式。 这意味着,当实例化正则表达式对象时或当调用一种静态正则表达式方法并且在缓存中找不到该正则表达式时,正则表达式引擎会将该正则表达式转换为一组中间操作代码,这些代码之后会转换为 MSIL。 调用方法时,JIT 编译器将执行该 MSIL。 与已解释的正则表达式相比,已编译的正则表达式增加了启动时间,但执行各种模式匹配方法的速度更快。 因此,相对于调用的正则表达式方法的数量,因编译正则表达式而产生的性能产生了改进。
简言之,当你使用特定正则表达式调用正则表达式方法相对不频繁时,建议使用已解释的正则表达式。 当你使用特定正则表达式调用正则表达式方法相对频繁时,应使用已编译的正则表达式。 很难确定已解释的正则表达式执行速度减慢超出启动时间减少带来的性能增益的确切阈值,或已编译的正则表达式启动速度减慢超出执行速度加快带来的性能增益的阈值。 这依赖于各种因素,包括正则表达式的复杂程度和它处理的特定数据。 若要确定已解释或已编译的正则表达式是否可为特定应用程序方案提供最佳性能,可以使用 Stopwatch 类来比较其执行时间。
下面的示例将编译和解释正则表达式时读取前十个句子和的性能进行比较,在读取 Theodore 的文本中的所有句子时金融家。 如示例输出所示,当只对匹配方法的正则表达式进行十次调用时,已解释的正则表达式与已编译的正则表达式相比,可提供更好的性能。 但是,当进行大量调用(在此示例中,超过 13,000 次调用)时,已编译的正则表达式可提供更好的性能。
using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]";
Stopwatch sw;
Match match;
int ctr;
StreamReader inFile = new StreamReader(@".\Dreiser_TheFinancier.txt");
string input = inFile.ReadToEnd();
inFile.Close();
// Read first ten sentences with interpreted regex.
Console.WriteLine("10 Sentences with Interpreted Regex:");
sw = Stopwatch.StartNew();
Regex int10 = new Regex(pattern, RegexOptions.Singleline);
match = int10.Match(input);
for (ctr = 0; ctr <= 9; ctr++) {
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine(" {0} matches in {1}", ctr, sw.Elapsed);
// Read first ten sentences with compiled regex.
Console.WriteLine("10 Sentences with Compiled Regex:");
sw = Stopwatch.StartNew();
Regex comp10 = new Regex(pattern,
RegexOptions.Singleline | RegexOptions.Compiled);
match = comp10.Match(input);
for (ctr = 0; ctr <= 9; ctr++) {
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine(" {0} matches in {1}", ctr, sw.Elapsed);
// Read all sentences with interpreted regex.
Console.WriteLine("All Sentences with Interpreted Regex:");
sw = Stopwatch.StartNew();
Regex intAll = new Regex(pattern, RegexOptions.Singleline);
match = intAll.Match(input);
int matches = 0;
while (match.Success) {
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine(" {0:N0} matches in {1}", matches, sw.Elapsed);
// Read all sentnces with compiled regex.
Console.WriteLine("All Sentences with Compiled Regex:");
sw = Stopwatch.StartNew();
Regex compAll = new Regex(pattern,
RegexOptions.Singleline | RegexOptions.Compiled);
match = compAll.Match(input);
matches = 0;
while (match.Success) {
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine(" {0:N0} matches in {1}", matches, sw.Elapsed);
}
}
// The example displays the following output:
// 10 Sentences with Interpreted Regex:
// 10 matches in 00:00:00.0047491
// 10 Sentences with Compiled Regex:
// 10 matches in 00:00:00.0141872
// All Sentences with Interpreted Regex:
// 13,443 matches in 00:00:01.1929928
// All Sentences with Compiled Regex:
// 13,443 matches in 00:00:00.7635869
//
// >compare1
// 10 Sentences with Interpreted Regex:
// 10 matches in 00:00:00.0046914
// 10 Sentences with Compiled Regex:
// 10 matches in 00:00:00.0143727
// All Sentences with Interpreted Regex:
// 13,443 matches in 00:00:01.1514100
// All Sentences with Compiled Regex:
// 13,443 matches in 00:00:00.7432921
Imports System.Diagnostics
Imports System.IO
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]"
Dim sw As Stopwatch
Dim match As Match
Dim ctr As Integer
Dim inFile As New StreamReader(".\Dreiser_TheFinancier.txt")
Dim input As String = inFile.ReadToEnd()
inFile.Close()
' Read first ten sentences with interpreted regex.
Console.WriteLine("10 Sentences with Interpreted Regex:")
sw = Stopwatch.StartNew()
Dim int10 As New Regex(pattern, RegexOptions.SingleLine)
match = int10.Match(input)
For ctr = 0 To 9
If match.Success Then
' Do nothing with the match except get the next match.
match = match.NextMatch()
Else
Exit For
End If
Next
sw.Stop()
Console.WriteLine(" {0} matches in {1}", ctr, sw.Elapsed)
' Read first ten sentences with compiled regex.
Console.WriteLine("10 Sentences with Compiled Regex:")
sw = Stopwatch.StartNew()
Dim comp10 As New Regex(pattern,
RegexOptions.SingleLine Or RegexOptions.Compiled)
match = comp10.Match(input)
For ctr = 0 To 9
If match.Success Then
' Do nothing with the match except get the next match.
match = match.NextMatch()
Else
Exit For
End If
Next
sw.Stop()
Console.WriteLine(" {0} matches in {1}", ctr, sw.Elapsed)
' Read all sentences with interpreted regex.
Console.WriteLine("All Sentences with Interpreted Regex:")
sw = Stopwatch.StartNew()
Dim intAll As New Regex(pattern, RegexOptions.SingleLine)
match = intAll.Match(input)
Dim matches As Integer = 0
Do While match.Success
matches += 1
' Do nothing with the match except get the next match.
match = match.NextMatch()
Loop
sw.Stop()
Console.WriteLine(" {0:N0} matches in {1}", matches, sw.Elapsed)
' Read all sentnces with compiled regex.
Console.WriteLine("All Sentences with Compiled Regex:")
sw = Stopwatch.StartNew()
Dim compAll As New Regex(pattern,
RegexOptions.SingleLine Or RegexOptions.Compiled)
match = compAll.Match(input)
matches = 0
Do While match.Success
matches += 1
' Do nothing with the match except get the next match.
match = match.NextMatch()
Loop
sw.Stop()
Console.WriteLine(" {0:N0} matches in {1}", matches, sw.Elapsed)
End Sub
End Module
' The example displays output like the following:
' 10 Sentences with Interpreted Regex:
' 10 matches in 00:00:00.0047491
' 10 Sentences with Compiled Regex:
' 10 matches in 00:00:00.0141872
' All Sentences with Interpreted Regex:
' 13,443 matches in 00:00:01.1929928
' All Sentences with Compiled Regex:
' 13,443 matches in 00:00:00.7635869
'
' >compare1
' 10 Sentences with Interpreted Regex:
' 10 matches in 00:00:00.0046914
' 10 Sentences with Compiled Regex:
' 10 matches in 00:00:00.0143727
' All Sentences with Interpreted Regex:
' 13,443 matches in 00:00:01.1514100
' All Sentences with Compiled Regex:
' 13,443 matches in 00:00:00.7432921
该示例中使用的正则表达式模式 \b(\w+((\r?\n)|,?\s))*\w+[.?:;!]
的定义如下表所示。
模式 | 描述 |
---|---|
\b | 在单词边界处开始匹配。 |
\w+ | 匹配一个或多个单词字符。 |
(\r?\n)|,?\s) | 匹配零个或一个回车符后跟一个换行符,或零个或一个逗号后跟一个空白字符。 |
(\w+((\r?\n)|,?\s))* | 匹配一个或多个单词字符的零个或多个事例,后跟零个或一个回车符和换行符,或后跟零个或一个逗号、一个空格字符。 |
\w+ | 匹配一个或多个单词字符。 |
[.?:;!] | 匹配句号、问号、冒号、分号或感叹号。 |
正则表达式:编译为程序集
.NET 还可以创建一个包含已编译的正则表达式的程序集。 这样会将正则表达式编译对性能造成的影响从运行时转移到设计时。 但是,这还涉及一些其他工作:必须提前定义正则表达式并将其编译为程序集。 然后,编译器在编译使用该程序集的正则表达式的源代码时,可以引用此程序集。 程序集内的每个已编译正则表达式都由从 Regex 派生的类来表示。
若要将正则表达式编译为程序集,可调用 Regex.CompileToAssembly(RegexCompilationInfo[], AssemblyName) 方法并向其传递表示要编译的正则表达式的 RegexCompilationInfo 对象数组和包含有关要创建的程序集的信息的 AssemblyName 对象。
建议你在以下情况下将正则表达式编译为程序集:
- 如果你是要创建可重用正则表达式库的组件开发人员。
- 如果你预期正则表达式的模式匹配方法要被调用的次数无法确定 -- 从任意位置,次数可能为一次两次到上千上万次。 与已编译或已解释的正则表达式不同,编译为单独程序集的正则表达式可提供与方法调用数量无关的一致性能。
如果使用已编译的正则表达式来优化性能,则不应使用反射来创建程序集,加载正则表达式引擎并执行其模式匹配方法。 这要求你避免动态生成正则表达式模式,并且要在创建程序集时指定模式匹配选项(如不区分大小写的模式匹配)。 它还要求将创建程序集的代码与使用正则表达式的代码分离。
下面的示例演示如何创建包含已编译的正则表达式的程序集。 它将创建名为程序集 RegexLib.dll
与单个正则表达式类, SentencePattern
,,其中包含句子匹配的正则表达式模式中使用 解释 vs。已编译的正则表达式 部分中使用的句子匹配的正则表达式模式。
using System;
using System.Reflection;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
RegexCompilationInfo SentencePattern =
new RegexCompilationInfo(@"\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]",
RegexOptions.Multiline,
"SentencePattern",
"Utilities.RegularExpressions",
true);
RegexCompilationInfo[] regexes = { SentencePattern };
AssemblyName assemName = new AssemblyName("RegexLib, Version=1.0.0.1001, Culture=neutral, PublicKeyToken=null");
Regex.CompileToAssembly(regexes, assemName);
}
}
Imports System.Reflection
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim SentencePattern As New RegexCompilationInfo("\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]",
RegexOptions.Multiline,
"SentencePattern",
"Utilities.RegularExpressions",
True)
Dim regexes() As RegexCompilationInfo = {SentencePattern}
Dim assemName As New AssemblyName("RegexLib, Version=1.0.0.1001, Culture=neutral, PublicKeyToken=null")
Regex.CompileToAssembly(regexes, assemName)
End Sub
End Module
在将示例编译为可执行文件并运行时,它会创建一个名为 RegexLib.dll
的程序集。 正则表达式用名为 Utilities.RegularExpressions.SentencePattern
并由 Regex 派生的类来表示。 下面的示例然后使用已编译的正则表达式从 Theodore 文本中提取句子金融家。
using System;
using System.IO;
using System.Text.RegularExpressions;
using Utilities.RegularExpressions;
public class Example
{
public static void Main()
{
SentencePattern pattern = new SentencePattern();
StreamReader inFile = new StreamReader(@".\Dreiser_TheFinancier.txt");
string input = inFile.ReadToEnd();
inFile.Close();
MatchCollection matches = pattern.Matches(input);
Console.WriteLine("Found {0:N0} sentences.", matches.Count);
}
}
// The example displays the following output:
// Found 13,443 sentences.
Imports System.IO
Imports System.Text.RegularExpressions
Imports Utilities.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As New SentencePattern()
Dim inFile As New StreamReader(".\Dreiser_TheFinancier.txt")
Dim input As String = inFile.ReadToEnd()
inFile.Close()
Dim matches As MatchCollection = pattern.Matches(input)
Console.WriteLine("Found {0:N0} sentences.", matches.Count)
End Sub
End Module
' The example displays the following output:
' Found 13,443 sentences.
控制回溯
通常,正则表达式引擎使用线性进度在输入字符串中移动并将其编译为正则表达式模式。 但是,当在正则表达式模式中使用不确定限定符(如 *
、 +
和 ?
)时,正则表达式引擎可能会放弃一部分成功的分部匹配,并返回以前保存的状态,以便为整个模式搜索成功匹配。 此过程称为回溯。
备注
有关回溯的详细信息,请参阅 的正则表达式行为的详细信息 和 回溯 。 有关回溯的详细讨论,请参阅 优化正则表达式性能,第 ii 部分: 采用控制回溯 BCL 团队博客中。
支持回溯可为正则表达式提供强大的功能和灵活性。 还可将控制正则表达式引擎操作的职责交给正则表达式开发人员来处理。 由于开发人员通常不了解此职责,因此其误用回溯或依赖过多回溯通常会显著降低正则表达式的性能。 在最糟糕的情况下,输入字符串中每增加一个字符,执行时间会加倍。 实际上,如果过多使用回溯,则在输入与正则表达式模式近似匹配时很容易创建无限循环的编程等效形式;正则表达式引擎可能需要几小时甚至几天来处理相对短的输入字符串。
通常,尽管回溯不是匹配所必需的,但应用程序会因使用回溯而对性能产生负面影响。 例如,正则表达式 \b\p{Lu}\w*\b
将匹配以大写字符开头的所有单词,如下表所示。
模式 | 描述 |
---|---|
\b | 在单词边界处开始匹配。 |
\p{Lu} | 匹配大写字符。 |
\w* | 匹配零个或多个单词字符。 |
\b | 在单词边界处结束匹配。 |
由于单词边界与单词字符不同也不是其子集,因此正则表达式引擎在匹配单词字符时无法跨越单词边界。 这意味着,对于此正则表达式而言,回溯对任何匹配的总体成功不会有任何贡献 -- 由于正则表达式引擎被强制为单词字符的每个成功的初步匹配保存其状态,因此它只会降低性能。
如果你确定,回溯是不必要,你可以通过使用禁用它 (?>``subexpression``)
语言元素。 下面的示例通过使用两个正则表达式来分析输入字符串。 第一个正则表达式 \b\p{Lu}\w*\b
依赖于回溯。 第二个正则表达式 \b\p{Lu}(?>\w*)\b
禁用回溯。 如示例输出所示,这两个正则表达式产生的结果相同。
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string input = "This this word Sentence name Capital";
string pattern = @"\b\p{Lu}\w*\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
Console.WriteLine();
pattern = @"\b\p{Lu}(?>\w*)\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// This
// Sentence
// Capital
//
// This
// Sentence
// Capital
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This this word Sentence name Capital"
Dim pattern As String = "\b\p{Lu}\w*\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
Console.WriteLine()
pattern = "\b\p{Lu}(?>\w*)\b"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine(match.Value)
Next
End Sub
End Module
' The example displays the following output:
' This
' Sentence
' Capital
'
' This
' Sentence
' Capital
在许多情况下,在将正则表达式模式与输入文本匹配时,回溯很重要。 但是,过度回溯会严重降低性能,并且会产生应用程序已停止响应的感觉。 特别需要指出的是,当嵌套限定符并且与外部子表达式匹配的文本为与内部子表达式匹配的文本的子集时,尤其会出现这种情况。
警告
除避免过度回溯之外,还应使用超时功能以确保过度回溯不会严重降低正则表达式性能。 有关详细信息,请参阅 使用超时值 部分。
例如,正则表达式模式 ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$
用于匹配至少包括一个字母数字字符的部件号。 任何附加字符可以包含字母数字字符、连字符、下划线或句号,但最后一个字符必须为字母数字。 美元符号用于终止部件号。 在某些情况下,由于限定符嵌套并且子表达式 [0-9A-Z]
是子表达式 [-.\w]*
的子集,因此此正则表达式模式会表现出极差的性能。
在这些情况下,可通过移除嵌套限定符并将外部子表达式替换为零宽度预测先行和回顾断言来优化正则表达式性能。 预测先行和回顾断言是定位点;它们不在输入字符串中移动指针,而是通过预测先行或回顾来检查是否满足指定条件。 例如,可将部件号正则表达式重写为 ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$
。 此正则表达式模式的定义如下表所示。
模式 | 说明 |
---|---|
^ | 从输入字符串的开头部分开始匹配。 |
[0-9A-Z] | 匹配字母数字字符。 部件号至少要包含此字符。 |
[-.\w]* | 匹配零个或多个任意单词字符、连字符或句号。 |
\$ | 匹配美元符号。 |
(?<=[0-9A-Z]) | 查看作为结束的美元符号,以确保前一个字符是字母数字。 |
$ | 在输入字符串末尾结束匹配。 |
下面的示例演示了如何使用此正则表达式来匹配包含可能部件号的数组。
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$";
string[] partNos = { "A1C$", "A4", "A4$", "A1603D$", "A1603D#" };
foreach (var input in partNos) {
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine(match.Value);
else
Console.WriteLine("Match not found.");
}
}
}
// The example displays the following output:
// A1C$
// Match not found.
// A4$
// A1603D$
// Match not found.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim pattern As String = "^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$"
Dim partNos() As String = { "A1C$", "A4", "A4$", "A1603D$",
"A1603D#" }
For Each input As String In partNos
Dim match As Match = Regex.Match(input, pattern)
If match.Success Then
Console.WriteLine(match.Value)
Else
Console.WriteLine("Match not found.")
End If
Next
End Sub
End Module
' The example displays the following output:
' A1C$
' Match not found.
' A4$
' A1603D$
' Match not found.
.NET 中的正则表达式语言包括以下可用于消除嵌套限定符的语言元素。 有关详细信息,请参阅 分组构造 。
语言元素 | 描述 |
---|---|
(?= subexpression ) | 零宽度正预测先行。 查看当前位置,以确定 subexpression 是否与输入字符串匹配。 |
(?! subexpression ) | 零宽度负预测先行。 查看当前位置,以确定 subexpression 是否不与输入字符串匹配。 |
(?<= subexpression ) | 零宽度正回顾。 回顾当前位置,以确定 subexpression 是否与输入字符串匹配。 |
(?<! subexpression ) | 零宽度负回顾。 回顾当前位置,以确定 subexpression 是否不与输入字符串匹配。 |
使用超时值
如果正则表达式处理与正则表达式模式大致匹配的输入,则通常依赖于会严重影响其性能的过度回溯。 除认真考虑对回溯的使用以及针对大致匹配输入对正则表达式进行测试之外,还应始终设置一个超时值以确保最大程度地降低过度回溯的影响(如果有)。
正则表达式超时间隔定义了在超时前正则表达式引擎用于查找单个匹配项的时间长度。默认超时间隔为 Regex.InfiniteMatchTimeout ,这意味着正则表达式不会超时。可以按如下所示重写此值并定义超时间隔:
- 在实例化一个 Regex 对象(通过调用 Regex.Regex(String, RegexOptions, TimeSpan) 构造函数)时,提供一个超时值。
- 调用静态模式匹配方法,如 Regex.Match(String, String, RegexOptions, TimeSpan) 或 Regex.Replace(String, String, String, RegexOptions, TimeSpan) ,其中包含
matchTimeout
参数。 - 对于通过调用 Regex.CompileToAssembly 方法创建的已编译的正则表达式,可调用带有 TimeSpan 类型的参数的构造函数。
如果定义了超时间隔并且在此间隔结束时未找到匹配项,则正则表达式方法将引发 RegexMatchTimeoutException 异常。 在异常处理程序中,可以选择使用一个更长的超时间隔来重试匹配、放弃匹配尝试并假定没有匹配项,或者放弃匹配尝试并记录异常信息以供未来分析。
下面的示例定义了一种 GetWordData
方法,此方法实例化了一个正则表达式,使其具有 350 毫秒的超时间隔,用于计算文本文件中的词语数和一个词语中的平均字符数。 如果匹配操作超时,则超时间隔将延长 350 毫秒并重新实例化 Regex 对象。 如果新的超时间隔超过 1 秒,则此方法将再次向调用方引发异常。
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
RegexUtilities util = new RegexUtilities();
string title = "Doyle - The Hound of the Baskervilles.txt";
try {
var info = util.GetWordData(title);
Console.WriteLine("Words: {0:N0}", info.Item1);
Console.WriteLine("Average Word Length: {0:N2} characters", info.Item2);
}
catch (IOException e) {
Console.WriteLine("IOException reading file '{0}'", title);
Console.WriteLine(e.Message);
}
catch (RegexMatchTimeoutException e) {
Console.WriteLine("The operation timed out after {0:N0} milliseconds",
e.MatchTimeout.TotalMilliseconds);
}
}
}
public class RegexUtilities
{
public Tuple<int, double> GetWordData(string filename)
{
const int MAX_TIMEOUT = 1000; // Maximum timeout interval in milliseconds.
const int INCREMENT = 350; // Milliseconds increment of timeout.
List<string> exclusions = new List<string>( new string[] { "a", "an", "the" });
int[] wordLengths = new int[29]; // Allocate an array of more than ample size.
string input = null;
StreamReader sr = null;
try {
sr = new StreamReader(filename);
input = sr.ReadToEnd();
}
catch (FileNotFoundException e) {
string msg = String.Format("Unable to find the file '{0}'", filename);
throw new IOException(msg, e);
}
catch (IOException e) {
throw new IOException(e.Message, e);
}
finally {
if (sr != null) sr.Close();
}
int timeoutInterval = INCREMENT;
bool init = false;
Regex rgx = null;
Match m = null;
int indexPos = 0;
do {
try {
if (! init) {
rgx = new Regex(@"\b\w+\b", RegexOptions.None,
TimeSpan.FromMilliseconds(timeoutInterval));
m = rgx.Match(input, indexPos);
init = true;
}
else {
m = m.NextMatch();
}
if (m.Success) {
if ( !exclusions.Contains(m.Value.ToLower()))
wordLengths[m.Value.Length]++;
indexPos += m.Length + 1;
}
}
catch (RegexMatchTimeoutException e) {
if (e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT) {
timeoutInterval += INCREMENT;
init = false;
}
else {
// Rethrow the exception.
throw;
}
}
} while (m.Success);
// If regex completed successfully, calculate number of words and average length.
int nWords = 0;
long totalLength = 0;
for (int ctr = wordLengths.GetLowerBound(0); ctr <= wordLengths.GetUpperBound(0); ctr++) {
nWords += wordLengths[ctr];
totalLength += ctr * wordLengths[ctr];
}
return new Tuple<int, double>(nWords, totalLength/nWords);
}
}
Imports System.Collections.Generic
Imports System.IO
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim util As New RegexUtilities()
Dim title As String = "Doyle - The Hound of the Baskervilles.txt"
Try
Dim info = util.GetWordData(title)
Console.WriteLine("Words: {0:N0}", info.Item1)
Console.WriteLine("Average Word Length: {0:N2} characters", info.Item2)
Catch e As IOException
Console.WriteLine("IOException reading file '{0}'", title)
Console.WriteLine(e.Message)
Catch e As RegexMatchTimeoutException
Console.WriteLine("The operation timed out after {0:N0} milliseconds",
e.MatchTimeout.TotalMilliseconds)
End Try
End Sub
End Module
Public Class RegexUtilities
Public Function GetWordData(filename As String) As Tuple(Of Integer, Double)
Const MAX_TIMEOUT As Integer = 1000 ' Maximum timeout interval in milliseconds.
Const INCREMENT As Integer = 350 ' Milliseconds increment of timeout.
Dim exclusions As New List(Of String)({"a", "an", "the" })
Dim wordLengths(30) As Integer ' Allocate an array of more than ample size.
Dim input As String = Nothing
Dim sr As StreamReader = Nothing
Try
sr = New StreamReader(filename)
input = sr.ReadToEnd()
Catch e As FileNotFoundException
Dim msg As String = String.Format("Unable to find the file '{0}'", filename)
Throw New IOException(msg, e)
Catch e As IOException
Throw New IOException(e.Message, e)
Finally
If sr IsNot Nothing Then sr.Close()
End Try
Dim timeoutInterval As Integer = INCREMENT
Dim init As Boolean = False
Dim rgx As Regex = Nothing
Dim m As Match = Nothing
Dim indexPos As Integer = 0
Do
Try
If Not init Then
rgx = New Regex("\b\w+\b", RegexOptions.None,
TimeSpan.FromMilliseconds(timeoutInterval))
m = rgx.Match(input, indexPos)
init = True
Else
m = m.NextMatch()
End If
If m.Success Then
If Not exclusions.Contains(m.Value.ToLower()) Then
wordLengths(m.Value.Length) += 1
End If
indexPos += m.Length + 1
End If
Catch e As RegexMatchTimeoutException
If e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT Then
timeoutInterval += INCREMENT
init = False
Else
' Rethrow the exception.
Throw
End If
End Try
Loop While m.Success
' If regex completed successfully, calculate number of words and average length.
Dim nWords As Integer
Dim totalLength As Long
For ctr As Integer = wordLengths.GetLowerBound(0) To wordLengths.GetUpperBound(0)
nWords += wordLengths(ctr)
totalLength += ctr * wordLengths(ctr)
Next
Return New Tuple(Of Integer, Double)(nWords, totalLength/nWords)
End Function
End Class
只在必要时捕获
.NET 中的正则表达式支持许多分组构造,这样,便可以将正则表达式模式分组为一个或多个子表达式。 在.NET 正则表达式语言中的最常用的分组构造是 (
子表达式)
,后者定义了已编号的捕获组,和 (?<
名称>
子表达式)
,后者定义了命名的捕获组。 分组构造是创建反向引用和定义要应用限定符的子表达式时所必需的。
但是,使用这些语言元素会产生一定的开销。 它们会导致用最近的未命名或已命名捕获来填充 GroupCollection 属性返回的 Match.Groups 对象,如果单个分组构造已捕获输入字符串中的多个子字符串,则还会填充包含多个 CaptureCollection 对象的特定捕获组的 Group.Captures 属性返回的 Capture 对象。
通常,只在正则表达式中使用分组构造,这样可对其应用限定符,而且以后不会使用这些子表达式捕获的组。 例如,正则表达式 \b(\w+[;,]?\s?)+[.?!]
用于捕获整个句子。 下表描述了此正则表达式模式中的语言元素及其对 Match 对象的 Match.Groups 和 Group.Captures 集合的影响。
模式 | 描述 |
---|---|
\b | 在单词边界处开始匹配。 |
\w+ | 匹配一个或多个单词字符。 |
[;,]? | 匹配零个或一个逗号或分号。 |
\s? | 匹配零个或一个空白字符。 |
(\w+[;,]?\s?)+ | 匹配以下一个或多个事例:一个或多个单词字符,后跟一个可选逗号或分号,一个可选的空白字符。 用于定义第一个捕获组,它是必需的,以便将重复多个单词字符的组合(即单词)后跟可选标点符号,直至正则表达式引擎到达句子末尾。 |
[.?!] | 匹配句号、问号或感叹号。 |
如下面的示例所示,当找到匹配时, GroupCollection 和 CaptureCollection 对象都将用匹配中的捕获内容来填充。 在此情况下,存在捕获组 (\w+[;,]?\s?)
,因此可对其应用 +
限定符,从而使得正则表达式模式可与句子中的每个单词匹配。 否则,它将匹配句子中的最后一个单词。
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string input = "This is one sentence. This is another.";
string pattern = @"\b(\w+[;,]?\s?)+[.?!]";
foreach (Match match in Regex.Matches(input, pattern)) {
Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index);
int grpCtr = 0;
foreach (Group grp in match.Groups) {
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index);
int capCtr = 0;
foreach (Capture cap in grp.Captures) {
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index);
capCtr++;
}
grpCtr++;
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: 'This is one sentence.' at index 0.
// Group 0: 'This is one sentence.' at index 0.
// Capture 0: 'This is one sentence.' at 0.
// Group 1: 'sentence' at index 12.
// Capture 0: 'This ' at 0.
// Capture 1: 'is ' at 5.
// Capture 2: 'one ' at 8.
// Capture 3: 'sentence' at 12.
//
// Match: 'This is another.' at index 22.
// Group 0: 'This is another.' at index 22.
// Capture 0: 'This is another.' at 22.
// Group 1: 'another' at index 30.
// Capture 0: 'This ' at 22.
// Capture 1: 'is ' at 27.
// Capture 2: 'another' at 30.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is one sentence. This is another."
Dim pattern As String = "\b(\w+[;,]?\s?)+[.?!]"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index)
Dim grpCtr As Integer = 0
For Each grp As Group In match.Groups
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index)
Dim capCtr As Integer = 0
For Each cap As Capture In grp.Captures
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index)
capCtr += 1
Next
grpCtr += 1
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' Match: 'This is one sentence.' at index 0.
' Group 0: 'This is one sentence.' at index 0.
' Capture 0: 'This is one sentence.' at 0.
' Group 1: 'sentence' at index 12.
' Capture 0: 'This ' at 0.
' Capture 1: 'is ' at 5.
' Capture 2: 'one ' at 8.
' Capture 3: 'sentence' at 12.
'
' Match: 'This is another.' at index 22.
' Group 0: 'This is another.' at index 22.
' Capture 0: 'This is another.' at 22.
' Group 1: 'another' at index 30.
' Capture 0: 'This ' at 22.
' Capture 1: 'is ' at 27.
' Capture 2: 'another' at 30.
当你只使用子表达式来对其应用限定符并且你对捕获的文本不感兴趣时,应禁用组捕获。 例如, (?:``subexpression``)
语言元素可防止应用到的组捕获匹配的子字符串。 在下面的示例中,上一示例中的正则表达式模式更改为 \b(?:\w+[;,]?\s?)+[.?!]
。 正如输出所示,它禁止正则表达式引擎填充 GroupCollection 和 CaptureCollection 集合。
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string input = "This is one sentence. This is another.";
string pattern = @"\b(?:\w+[;,]?\s?)+[.?!]";
foreach (Match match in Regex.Matches(input, pattern)) {
Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index);
int grpCtr = 0;
foreach (Group grp in match.Groups) {
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index);
int capCtr = 0;
foreach (Capture cap in grp.Captures) {
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index);
capCtr++;
}
grpCtr++;
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: 'This is one sentence.' at index 0.
// Group 0: 'This is one sentence.' at index 0.
// Capture 0: 'This is one sentence.' at 0.
//
// Match: 'This is another.' at index 22.
// Group 0: 'This is another.' at index 22.
// Capture 0: 'This is another.' at 22.
Imports System.Text.RegularExpressions
Module Example
Public Sub Main()
Dim input As String = "This is one sentence. This is another."
Dim pattern As String = "\b(?:\w+[;,]?\s?)+[.?!]"
For Each match As Match In Regex.Matches(input, pattern)
Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index)
Dim grpCtr As Integer = 0
For Each grp As Group In match.Groups
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index)
Dim capCtr As Integer = 0
For Each cap As Capture In grp.Captures
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index)
capCtr += 1
Next
grpCtr += 1
Next
Console.WriteLine()
Next
End Sub
End Module
' The example displays the following output:
' Match: 'This is one sentence.' at index 0.
' Group 0: 'This is one sentence.' at index 0.
' Capture 0: 'This is one sentence.' at 0.
'
' Match: 'This is another.' at index 22.
' Group 0: 'This is another.' at index 22.
' Capture 0: 'This is another.' at 22.
可以通过以下方式之一来禁用捕获:
- 使用
(?:``subexpression``)
语言元素。 此元素可防止在它应用的组中捕获匹配的子字符串。 它不在任何嵌套的组中禁用子字符串捕获。 - 使用 ExplicitCapture 选项。 在正则表达式模式中禁用所有未命名或隐式捕获。 当你使用此选项时,仅匹配的子字符串与定义的命名组
(?<``name``>``subexpression``)
可以捕获语言元素。 可将 ExplicitCapture 标记传递给options
类构造函数的 Regex 参数或options
静态匹配方法的 Regex 参数。 - 在
n
语言元素中使用(?imnsx)
选项。 此选项将在元素出现的正则表达式模式中的点处禁用所有未命名或隐式捕获。 捕获将一直禁用到模式结束或(-n)
选项启用未命名或隐式捕获。 有关详细信息,请参阅 其他构造 。 - 在
n
语言元素中使用(?imnsx:``subexpression``)
选项。 此选项可在subexpression
中禁用所有未命名或隐式捕获。 同时禁用任何未命名或隐式的嵌套捕获组进行的任何捕获。
相关主题
标题 | 描述 |
---|---|
正则表达式行为的详细信息 | 在 .NET 中检查正则表达式引擎的实现。 该主题重点介绍正则表达式的灵活性,并说明开发人员确保正则表达式引擎高效、强健运行的职责。 |
回溯 | 说明何为回溯及其对正则表达式性能有何影响,并检查为回溯提供替代项的语言元素。 |
正则表达式语言 - 快速参考 | 介绍 .NET 中的正则表达式语言的元素,并提供每个语言元素的详细文档链接。 |
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论