正则表达式语言标识字符模式。支持正则表达式的 .NET 类型基于 Perl 5 正则表达式,同时支持搜索和搜索/替换功能。
正则表达式用于以下任务:
- 验证文本输入,例如密码和电话号码
- 将文本数据分析为更结构化的形式(例如,NuGet 版本字符串)
- 替换文档中的文本模式(例如,仅整个单词)
本章分为两个概念部分,介绍 .NET 中正则表达式的基础知识,以及介绍正则表达式的参考部分。
所有正则表达式类型都在 System.Text.RegularExpressions 中定义。
注意
本章中的示例都预加载到 LINQPad 中,其中还包括一个交互式正则表达式工具(按 Ctrl+Shift+F1)。在线工具可在 获得。
正则表达式基础知识
最常见的正则表达式运算符之一是。?是与前面的项目 0 或 1 时间匹配的量词。换句话说,?表示。项目可以是单个字符,也可以是方括号中的复杂字符结构。例如,正则表达式 “colou?r” 匹配颜色和,但不匹配 colouur:
Console.WriteLine (Regex.Match ("color", @"colou?r").Success); // True
Console.WriteLine (Regex.Match ("colour", @"colou?r").Success); // True
Console.WriteLine (Regex.Match ("colouur", @"colou?r").Success); // False
Regex.Match 在较大的字符串中进行搜索。它返回的对象具有匹配项的索引和长度以及匹配的实际值的属性:
Match m = Regex.Match ("any colour you like", @"colou?r");
Console.WriteLine (m.Success); // True
Console.WriteLine (m.Index); // 4
Console.WriteLine (m.Length); // 6
Console.WriteLine (m.Value); // colour
Console.WriteLine (m.ToString()); // colour
你可以把Regex.Match看作是字符串的IndexOf方法的一个更强大的版本。不同之处在于它搜索而不是文字字符串。
方法是一个快捷方式,用于调用 Match,然后测试 Success 属性。
默认情况下,正则表达式引擎从左到右工作,因此仅返回最左侧的匹配项。可以使用 NextMatch 方法返回更多匹配项:
Match m1 = Regex.Match ("One color? There are two colours in my head!",
@"colou?rs?");
Match m2 = m1.NextMatch();
Console.WriteLine (m1); // color
Console.WriteLine (m2); // colours
方法返回数组中的所有匹配项。我们可以重写前面的示例,如下所示:
foreach (Match m in Regex.Matches
("One color? There are two colours in my head!", @"colou?rs?"))
Console.WriteLine (m);
另一个常见的正则表达式运算符是,用竖线 | 表示。交流发电机表示替代方案。以下匹配“Jen”、“Jenny”和“Jennifer”:
Console.WriteLine (Regex.IsMatch ("Jenny", "Jen(ny|nifer)?")); // True
交流发电机周围的括号将备选方案与表达式的其余部分分开。
注意
您可以在匹配正则表达式时指定超时。如果匹配操作花费的时间超过指定的 TimeSpan,则会抛出 RegexMatchTimeoutException。如果程序处理任意正则表达式(例如,在高级搜索对话框中),这会很有用,因为它可以防止格式错误的正则表达式无限旋转。
编译的正则表达式
在前面的一些示例中,我们使用相同的模式重复调用静态 RegEx 方法。在这些情况下,另一种方法是使用模式和 RegexOptions.Compiled 实例化 Regex 对象,然后调用实例:
Regex r = new Regex (@"sausages?", RegexOptions.Compiled);
Console.WriteLine (r.Match ("sausage")); // sausage
Console.WriteLine (r.Match ("sausages")); // sausages
RegexOptions.Compiled指示RegEx实例使用轻量级代码生成(Reflection.Emit中的DynamicMethod)来动态构建和编译针对该特定正则表达式定制的代码。这样可以加快匹配速度,但会降低初始编译成本。
您还可以实例化 Regex 对象,而无需使用 RegexOptions.Compile。正则表达式实例是不可变的。
注意
正则表达式引擎速度很快。即使没有编译,简单的匹配通常也不到一微秒。
正则表达式选项
正则表达式选项标志枚举允许您调整匹配行为。RegexOptions 的常见用途是执行不区分大小写的搜索:
Console.WriteLine (Regex.Match ("a", "A", RegexOptions.IgnoreCase)); // a
这将应用当前区域性的大小写等效规则。使用“区域性不变”标志可以请求固定区域性,改为:
Console.WriteLine (Regex.Match ("a", "A", RegexOptions.IgnoreCase
| RegexOptions.CultureInvariant));
您可以使用单字母代码激活正则表达式本身中的大多数 RegexOptions 标志,如下所示:
Console.WriteLine (Regex.Match ("a", @"(?i)A")); // a
您可以在整个表达式中打开和关闭选项:
Console.WriteLine (Regex.Match ("AAAa", @"(?i)a(?-i)a")); // Aa
另一个有用的选项是 IgnorePatternWhitespace 或 (?x) 。这允许您插入空格以使正则表达式更具可读性,而无需按字面意思获取空格。
列出了所有 RegExOptions 值及其单字母代码。
正则表达式选项 | ||
枚举值 | 正则表达式代码 | 描述 |
没有 | ||
忽略案例 | 我 | 忽略大小写(默认情况下,正则表达式区分大小写) |
多行 | m | 更改 ^ 和 $,以便它们匹配行的开头/结尾,而不是字符串的开头/结尾 |
显式捕获 | n | 仅捕获显式命名或显式编号的组(请参阅) |
编译 | 强制编译到 IL(请参阅) | |
单线 | s | 使。匹配每个字符(而不是匹配除 \n 之外的每个字符) |
忽略模式空白 | x | 从模式中消除未转义的空格 |
从右到左 | r | 从右到左搜索;无法在中游指定 |
ECMAScript | 强制符合 ECMA 标准(默认情况下,实现不符合 ECMA 标准) | |
文化不变 | 关闭字符串比较的区域性特定行为 |
角色转义
正则表达式具有以下元字符,这些元字符具有特殊含义而不是字面含义:
\ * + ? |{ [ () ^ $ .#
若要按字面意思使用元字符,必须使用反斜杠为字符前缀或。在下面的示例中,我们转义 ?字符以匹配字符串“什么?
Console.WriteLine (Regex.Match ("what?", @"what\?")); // what? (correct)
Console.WriteLine (Regex.Match ("what?", @"what?")); // what (incorrect)
注意
如果字符位于(方括号)内,则此规则不适用,并且元字符按字面解释。我们将在下一节中讨论集合。
正则表达式的 Escape 和 Unescape 方法通过用转义等价物替换它们来转换包含正则表达式元字符的字符串,反之亦然:
Console.WriteLine (Regex.Escape (@"?")); // \?
Console.WriteLine (Regex.Unescape (@"\?")); // ?>
本章中的所有正则表达式字符串都使用 C# @ 文本表示。这是为了绕过 C# 的转义机制,该机制也使用反斜杠。如果没有 @,文字反斜杠将需要四个反斜杠:
Console.WriteLine (Regex.Match ("\\", "\\\\")); // \
除非包含 (?x) 选项,否则空格在正则中按字面意思处理:
Console.Write (Regex.IsMatch ("hello world", @"hello world")); // True
字符集
字符集充当特定字符集的通配符。
表达 | 意义 | 反向(“非”) |
[美国广播公司] | 匹配列表中的单个字符。 | [^abcdef] |
[a-f] | 匹配区域中的单个字符 | [^a-f] |
\d | 匹配 Unicode 类别中的任何内容。在 ECMAScript 模式下,[0-9] . | \D |
\w | 匹配字符(默认情况下,根据 而有所不同;例如,在英语中,与 相同)。 | \W |
\s | 匹配空格字符;也就是说,任何 返回 true(包括 Unicode 空格)。在 ECMAScript 模式下,[\n\r\t\f\v ] 。 | \S |
\p{ } | 匹配指定中的字符。 | \P |
. | (默认模式)匹配除 \n 之外的任何字符。 | \n |
. | (单线模式)匹配任何字符。 | \n |
要完全匹配一组字符中的一个,请将字符集放在方括号中:
Console.Write (Regex.Matches ("That is that.", "[Tt]hat").Count); // 2
要匹配除集合中的字符的任何字符,请将集合放在方括号中,并在第一个字符之前加上 ^ 符号:
Console.Write (Regex.Match ("quiz qwerty", "q[^aeiou]").Index); // 5
可以使用连字符指定字符范围。以下正则表达式匹配国际象棋移动:
Console.Write (Regex.Match ("b1-c4", @"[a-h]\d-[a-h]\d").Success); // True
\d 表示数字字符,因此 \d 将匹配任何数字。\D 匹配任何非数字字符。
\w 表示单词字符,其中包括字母、数字和下划线。\W 匹配任何非单词字符。这些也适用于非英语字母,例如西里尔字母。
.匹配除 \n 以外的任何字符(但允许 \r )。
\p 匹配指定类别中的字符,例如 {Lu} 表示大写字母或 {P} 表示标点符号(我们将在本后面的参考部分中列出类别):
Console.Write (Regex.IsMatch ("Yes, please", @"\p{P}")); // True
我们将发现 \d 、\w 和 的更多用途。当我们将它们与结合使用时。
量词
量词与项目匹配指定的次数。
量词 | 意义 |
* | 零个或多个匹配项 |
+ | 一个或多个匹配项 |
? | 零个或一个匹配项 |
{n} | 完全匹配n |
{n,} | 至少匹配n |
{n,m} | 之间和匹配nm |
* 量词与前面的字符或组匹配零次或多次。以下内容匹配 ,以及同一文件的任何编号版本(例如,、):
Console.Write (Regex.Match ("cv15.docx", @"cv\d*\.docx").Success); // True
请注意,我们必须使用反斜杠转义文件扩展名中的句点。
以下允许 和 .docx 之间的任何内容,等效于 :
Console.Write (Regex.Match ("cvjoint.docx", @"cv.*\.docx").Success); // True
+ 量词与前面的字符或组匹配一次或多次。例如:
Console.Write (Regex.Matches ("slow! yeah slooow!", "slo+w").Count); // 2
{} 量词匹配指定的重复次数(或范围)。以下与血压读数匹配:
Regex bp = new Regex (@"\d{2,3}/\d{2,3}");
Console.WriteLine (bp.Match ("It used to be 160/110")); // 160/110
Console.WriteLine (bp.Match ("Now it's only 115/75")); // 115/75
贪婪与懒惰的量词
默认情况下,量词是的,而不是的。贪婪的量词在前进之前会重复惰性量词在前进之前尽可能地重复。您可以通过在 ?象征。为了说明差异,请考虑以下 HTML 片段:
string html = "By default quantifiers are greedy creatures";
假设我们要提取斜体中的两个短语。如果我们执行以下操作
foreach (Match m in Regex.Matches (html, @".*"))
Console.WriteLine (m);
结果不是两个匹配项,而是匹配项:
By default quantifiers are greedy
问题是我们的 * 量词在匹配 。因此,它正好经过第一个 ,仅在最后一个 (表达式的其余部分仍然可以匹配的)停止。
如果我们使量词变得惰性,则 * 会在表达式的其余部分可以匹配解围:
foreach (Match m in Regex.Matches (html, @".*?"))
Console.WriteLine (m);
结果如下:
By default
greedy
零宽度断言
正则表达式语言允许您通过、、和来设置匹配或应发生的情况的条件。这些断言称为零宽度断言,因为它们不会增加匹配本身的(或长度)。
前瞻和后瞻
构造检查后面的文本是否匹配,而不在结果中包含 expr。这称为。在下面的示例中,我们查找一个数字,后跟单词“miles”:(?=expr)expr
Console.WriteLine (Regex.Match ("say 25 miles more", @"\d+\s(?=miles)"));
OUTPUT: 25
请注意,结果中未返回单词“miles”,即使需要来满足匹配。
在成功后,匹配继续进行,就好像先睹为快从未发生过一样。所以,如果我们像这样将 .* 附加到我们的表达式中
Console.WriteLine (Regex.Match ("say 25 miles more", @"\d+\s(?=miles).*"));
结果是多了 25 英里.
前瞻可用于强制实施强密码的规则。假设密码必须至少为六个字符且至少包含一个数字。通过查找,我们可以实现这一点,如下所示:
string password = "...";
bool ok = Regex.IsMatch (password, @"(?=.*\d).{6,}");
这首先执行,以确保字符串中的某处出现数字。如果满意,它将返回到预览开始之前的位置,并匹配六个或更多字符。(中,我们包含一个更实质性的密码验证示例。
相反的是构造,.这要求匹配项后面跟有 。以下表达式与“好”匹配,除非字符串后面出现“但是”或“但是”:(?!expr)expr
string regex = "(?i)good(?!.*(however|but))";
Console.WriteLine (Regex.IsMatch ("Good work! But...", regex)); // False
Console.WriteLine (Regex.IsMatch ("Good work! Thanks!", regex)); // True
该构造表示并要求在匹配项有一个指定的表达式。相反的构造 表示,并要求匹配项指定的表达式。例如,以下匹配“良好”,除非“但是”出现在字符串的:(?<=expr)(?expr)
string regex = "(?i)(?
我们可以通过添加来改进这些示例,我们将稍后介绍。
锚
锚点 ^ 和 $ 匹配特定。默认情况下:
^
匹配字符串的
$
匹配字符串
注意
^ 有两个上下文相关的含义:点和。
$ 有两个上下文相关的含义:点和符。
例如:
Console.WriteLine (Regex.Match ("Not now", "^[Nn]o")); // No
Console.WriteLine (Regex.Match ("f = 0.2F", "[Ff]$")); // F
当您指定 RegexOptions.Multiline 或在表达式中包含 (?m) 时:
- ^ 匹配字符串或的开头(紧跟在 \n 之后)。
- $ 匹配字符串或的末尾(紧邻 \n 之前)。
在多行模式下使用 $ 有一个问题:Windows 中的新行几乎总是用 \r\n 表示,而不仅仅是 \n 。这意味着要使 $ 对 Windows 文件有用,您通常还必须匹配 \r ,并具有:
(?=\r?$)
可确保 \r 不会成为结果的一部分。以下匹配以“.txt”结尾的行:
string fileNames = "a.txt" + "\r\n" + "b.docx" + "\r\n" + "c.txt";
string r = @".+\.txt(?=\r?$)";
foreach (Match m in Regex.Matches (fileNames, r, RegexOptions.Multiline))
Console.Write (m + " ");
OUTPUT: a.txt c.txt
以下内容匹配字符串 s 中的所有空行:
MatchCollection emptyLines = Regex.Matches (s, "^(?=\r?$)",
RegexOptions.Multiline);
以下内容匹配所有空行或仅包含空格的行:
MatchCollection blankLines = Regex.Matches (s, "^[ \t]*(?=\r?$)",
RegexOptions.Multiline);
注意
由于锚点匹配位置而不是字符,因此指定锚点本身与空字符串匹配:
Console.WriteLine (Regex.Match ("x", "$").Length); // 0
单词边界
单词边界断言 \b 匹配单词字符 ( \w ) 相邻的位置:
- 非字字符 ( \W )
- 字符串的开头/结尾 ( ^ 和 $ )
\b 通常用于匹配整个单词:
foreach (Match m in Regex.Matches ("Wedding in Sarajevo", @"\b\w+\b"))
Console.WriteLine (m);
Wedding
in
Sarajevo
以下语句突出显示了字边界的效果:
int one = Regex.Matches ("Wedding in Sarajevo", @"\bin\b").Count; // 1
int two = Regex.Matches ("Wedding in Sarajevo", @"in").Count; // 2
下一个查询使用返回单词,后跟“(原文如此)”:
string text = "Don't loose (sic) your cool";
Console.Write (Regex.Match (text, @"\b\w+\b\s(?=\(sic\))")); // loose
组
有时,将正则表达式分成一系列子表达式或很有用。例如,请考虑以下表示美国电话号码(如 206-465-1918)的正则表达式:
\d{3}-\d{3}-\d{4}
假设我们想将其分为两组:区号和本地号码。我们可以通过使用括号来每个组来实现这一点:
(\d{3})-(\d{3}-\d{4})
然后,我们以编程方式检索组:
Match m = Regex.Match ("206-465-1918", @"(\d{3})-(\d{3}-\d{4})");
Console.WriteLine (m.Groups[1]); // 206
Console.WriteLine (m.Groups[2]); // 465-1918
第 0 组表示整个匹配项。换句话说,它与匹配项的值相同:
Console.WriteLine (m.Groups[0]); // 206-465-1918
Console.WriteLine (m); // 206-465-1918
组是正则表达式语言本身的一部分。这意味着您可以在正则表达式中引用组。\n 语法允许您在表达式中按组号 n 为组编制索引。例如,表达式 (\w)ee\1 匹配 deed 和 peep 。在下面的示例中,我们找到以同一字母开头和结尾的字符串中的所有单词:
foreach (Match m in Regex.Matches ("pop pope peep", @"\b(\w)\w+\1\b"))
Console.Write (m + " "); // pop peep
\w 周围的括号指示正则表达式引擎将子匹配存储在一个组中(在本例中为单个字母),以便以后可以使用。我们稍后使用 \1 来引用该组,表示表达式中的第一个组。
命名组
在长表达式或复杂表达式中,按而不是索引处理组可能更容易。这是对上一个示例的重写,使用我们命名为“letter”的组:
string regEx =
@"\b" + // word boundary
@"(?'letter'\w)" + // match first letter, and name it 'letter'
@"\w+" + // match middle letters
@"\k'letter'" + // match last letter, denoted by 'letter'
@"\b"; // word boundary
foreach (Match m in Regex.Matches ("bob pope peep", regEx))
Console.Write (m + " "); // bob peep
下面介绍如何命名捕获的组:
(?'group-name'group-expr) or (?group-expr)
以下是引用组的方法:
\k'group-name' or \k
下面的示例通过查找具有匹配名称的开始节点和结束节点来匹配简单(非嵌套)XML/HTML 元素:
string regFind =
@"<(?'tag'\w+?).*>" + // lazy-match first tag, and name it 'tag'
@"(?'text'.*?)" + // lazy-match text content, name it 'text'
@"\k'tag'>"; // match last tag, denoted by 'tag'
Match m = Regex.Match ("hello
", regFind);
Console.WriteLine (m.Groups ["tag"]); // h1
Console.WriteLine (m.Groups ["text"]); // hello
允许 XML 结构中的所有可能变体(如嵌套元素)更为复杂。.NET 正则表达式引擎有一个称为“匹配平衡构造”的复杂扩展,可以帮助嵌套标记 - 有关这方面的信息可在 Internet 上找到,也可以在 Jeffrey E. F. Friedl 的 (O'Reilly) 中找到。
替换和拆分文本
RegEx.Replace 方法的工作方式类似于字符串。替换,但使用正则表达式除外。
以下将“猫”替换为“狗”。与字符串不同。替换 ,“catapult”不会变成 “dogapult”,因为我们在单词边界上匹配:
string find = @"\bcat\b";
string replace = "dog";
Console.WriteLine (Regex.Replace ("catapult the cat", find, replace));
OUTPUT: catapult the dog
替换字符串可以使用 $0 替换构造引用原始匹配项。下面的示例将字符串中的数字括在尖括号中:
string text = "10 plus 20 makes 30";
Console.WriteLine (Regex.Replace (text, @"\d+", @"<$0>"));
OUTPUT: <10> plus <20> makes <30>
您可以使用 $1 、$2、$3 等访问任何捕获的组,也可以访问命名组。为了说明这如何有用,请考虑上一节中与简单 XML 元素匹配的正则表达式。通过重新排列组,我们可以形成一个替换表达式,将元素的内容移动到 XML 属性中:${name}
string regFind =
@"<(?'tag'\w+?).*>" + // lazy-match first tag, and name it 'tag'
@"(?'text'.*?)" + // lazy-match text content, name it 'text'
@"\k'tag'>"; // match last tag, denoted by 'tag'
string regReplace =
@"<${tag}" + // "; // "/>
Console.Write (Regex.Replace ("hello ", regFind, regReplace));
结果如下:
比赛评估器委托
替换有一个重载,该重载采用匹配评估器委托,该委托按匹配项调用。这允许您在正则表达式语言表现力不够时将替换字符串的内容委托给 C# 代码:
Console.WriteLine (Regex.Replace ("5 is less than 10", @"\d+",
m => (int.Parse (m.Value) * 10).ToString()) );
OUTPUT: 50 is less than 100
在中,我们展示了如何使用 MatchEvaluator 为 HTML 适当地转义 Unicode 字符。
拆分文本
静态 Regex.Split 方法是字符串的更强大的版本。拆分方法,正则表达式表示分隔符模式。在这个例子中,我们拆分一个字符串,其中任何数字都算作分隔符:
foreach (string s in Regex.Split ("a5b7c", @"\d"))
Console.Write (s + " "); // a b c
此处的结果不包括分隔符本身。但是,您可以通过将表达式包装在中来包含分隔符。下面将驼峰大小写字符串拆分为单独的单词:
foreach (string s in Regex.Split ("oneTwoThree", @"(?=[A-Z])"))
Console.Write (s + " "); // one Two Three
说明书正则表达式
食谱
匹配美国社会安全号码/电话号码string ssNum = @"\d{3}-\d{2}-\d{4}"; Console.WriteLine (Regex.IsMatch ("123-45-6789", ssNum)); // True string phone = @"(?x) ( \d{3}[-\s] | \(\d{3}\)\s? ) \d{3}[-\s]? \d{4}"; Console.WriteLine (Regex.IsMatch ("123-456-7890", phone)); // True Console.WriteLine (Regex.IsMatch ("(123) 456-7890", phone)); // True
提取“名称 = 值”对(每行一个)
请注意,这以指令 (?m) 开头:
string r = @"(?m)^\s*(?'name'\w+)\s*=\s*(?'value'.*)\s*(?=\r?$)";
string text =
@"id = 3
secure = true
timeout = 30";
foreach (Match m in Regex.Matches (text, r))
Console.WriteLine (m.Groups["name"] + " is " + m.Groups["value"]);
id is 3 secure is true timeout is 30
强密码验证
下面检查密码是否至少包含六个字符,以及密码是否包含数字、符号或标点符号:
string r = @"(?x)^(?=.* ( \d | \p{P} | \p{S} )).{6,}";
Console.WriteLine (Regex.IsMatch ("abc12", r)); // False
Console.WriteLine (Regex.IsMatch ("abcdef", r)); // False
Console.WriteLine (Regex.IsMatch ("ab88yz", r)); // True
至少 80 个字符的行string r = @"(?m)^.{80,}(?=\r?$)"; string fifty = new string ('x', 50); string eighty = new string ('x', 80); string text = eighty + "\r\n" + fifty + "\r\n" + eighty; Console.WriteLine (Regex.Matches (text, r).Count); // 2
解析日期/时间(N/N/N H:M:S AM/PM)
此表达式处理各种数字日期格式,无论年份排在第一位还是最后一年,该表达式都有效。(?x) 指令通过允许空格来提高可读性;(?i) 关闭区分大小写(对于可选的 AM/PM 指示符)。然后,您可以通过组集合访问比赛的每个组件:
string r = @"(?x)(?i)
(\d{1,4}) [./-]
(\d{1,2}) [./-]
(\d{1,4}) [\sT]
(\d+):(\d+):(\d+) \s? (A\.?M\.?|P\.?M\.?)?";
string text = "01/02/2008 5:20:50 PM";
foreach (Group g in Regex.Match (text, r).Groups)
Console.WriteLine (g.Value + " ");
01/02/2008 5:20:50 PM 01 02 2008 5 20 50 PM
(当然,这不会验证日期/时间是否正确。
匹配的罗马数字string r = @"(?i)\bm*" + @"(d?c{0,3}|c[dm])" + @"(l?x{0,3}|x[lc])" + @"(v?i{0,3}|i[vx])" + @"\b"; Console.WriteLine (Regex.IsMatch ("MCMLXXXIV", r)); // True
删除重复的单词
在这里,我们捕获一个名为 dupe 的命名组:
string r = @"(?'dupe'\w+)\W\k'dupe'";
string text = "In the the beginning...";
Console.WriteLine (Regex.Replace (text, r, "${dupe}"));
In the beginning
字数string r = @"\b(\w|[-'])+\b"; string text = "It's all mumbo-jumbo to me"; Console.WriteLine (Regex.Matches (text, r).Count); // 5
匹配 GUIDstring r = @"(?i)\b" + @"[0-9a-fA-F]{8}\-" + @"[0-9a-fA-F]{4}\-" + @"[0-9a-fA-F]{4}\-" + @"[0-9a-fA-F]{4}\-" + @"[0-9a-fA-F]{12}" + @"\b"; string text = "Its key is {3F2504E0-4F89-11D3-9A0C-0305E82C3301}."; Console.WriteLine (Regex.Match (text, r).Index); // 12
解析 XML/HTML 标记
正则表达式对于解析 HTML 片段很有用,尤其是当文档的格式可能不完美时:
string r =
@"<(?'tag'\w+?).*>" + // lazy-match first tag, and name it 'tag'
@"(?'text'.*?)" + // lazy-match text content, name it 'textd'
@"\k'tag'>"; // match last tag, denoted by 'tag'
string text = "hello
";
Match m = Regex.Match (text, r);
Console.WriteLine (m.Groups ["tag"]); // h1
Console.WriteLine (m.Groups ["text"]); // hello
拆分驼峰大小写的单词
这需要以包含大写分隔符:
string r = @"(?=[A-Z])";
foreach (string s in Regex.Split ("oneTwoThree", r))
Console.Write (s + " "); // one Two Three
获取合法文件名string input = "My \"good\"
转义 HTML 的 Unicode 字符string htmlFragment = "? 2007"; string result = Regex.Replace (htmlFragment, @"[\u0080-\uFFFF]", m => @"" + ((int)m.Value[0]).ToString() + ";"); Console.WriteLine (result); // ? 2007
HTTP 查询字符串中的取消转义字符string sample = "C%23 rocks"; string result = Regex.Replace ( sample, @"%[0-9a-f][0-9a-f]", m => ((char) Convert.ToByte (m.Value.Substring (1), 16)).ToString(), RegexOptions.IgnoreCase ); Console.WriteLine (result); // C# rocks
从网络统计信息日志中解析 Google 搜索字词
应将其与前面的示例结合使用,以取消查询字符串中的字符转义:
string sample =
"http://google.com/search?hl=en&q=greedy+quantifiers+regex&btnG=Search";
Match m = Regex.Match (sample, @"(?<=google\..+search\?.*q=).+?(?=(&|$))");
string[] keywords = m.Value.Split (
new[] { '+' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string keyword in keywords)
Console.Write (keyword + " "); // greedy quantifiers regex
正则表达式语言参考
表 25-2 到 汇总了 .NET 实现中支持的正则表达式语法和语法。
角色转义 | ||
转义代码序列 | 意义 | 十六进制等效 |
\一个 | 钟 | \u0007 |
\b | 退格 | \u0008 |
\t | 标签 | \u0009 |
\r | 回车 | \u000A |
\v | 垂直选项卡 | \u000B |
\f | 表单馈送 | \u000C |
\n | 换行符 | \u000D |
\e | 逃 | \u001B |
\nnn | ASCII 字符 作为八进制(例如,\n052 ) | |
\xnn | ASCII 字符 作为十六进制(例如 \x3F) | |
\cl | ASCII 控制字符 (例如,\cG 表示 Ctrl-G) | |
\unnnn | Unicode 字符 作为十六进制(例如,\u07DE ) | |
\symbol | 非转义符号 |
特殊情况:在正则表达式中,\b 表示单词边界,但在 [ ] 集中除外,其中 \b 表示退格字符。
字符集 | ||
表达 | 意义 | 反向(“非”) |
[美国广播公司] | 匹配列表中的单个字符 | [^abcdef] |
[a-f] | 匹配的单个字符 | [^a-f] |
\d | 匹配十进制数字 与 [0-9] 相同 | \D |
\w | 匹配字符(默认情况下,根据 而有所不同;例如,在英语中,与 [a-zA-Z_0-9] 相同) | \W |
\s | 匹配空格字符 与 [\n\r\t\f\v ] 相同 | \S |
\p{category} | 匹配指定中的字符(请参阅) | \P |
. | (默认模式)匹配除 \n 之外的任何字符 | \n |
. | (单线模式)匹配任何字符 | \n |
字符类别 | |
量词 | 意义 |
\p{L} | 函件 |
\p{Lu} | 大写字母 |
\p{Ll} | 小写字母 |
\p{N} | 数字 |
\p{P} | 标点 |
\p{M} | 音调符号 |
\p{S} | 符号 |
\p{Z} | 分隔符 |
\p{C} | 控制字符 |
量词 | |
量词 | 意义 |
* | 零个或多个匹配项 |
+ | 一个或多个匹配项 |
? | 零个或一个匹配项 |
{n} | 完全匹配n |
{n,} | 至少匹配n |
{n,m} | 之间和匹配nm |
这?后缀可以应用于任何量词,使它们而不是。
替换 | |
表达 | 意义 |
$0 | 替换匹配的文本 |
$group-number | 替换匹配文本中的索引group-number |
${group-name} | 替换匹配文本中的文本group-name |
替换仅在替换模式中指定。
零宽度断言 | |
表达 | 意义 |
^ | 字符串开头(或多行模式下的) |
$ | 字符串末尾(或多行模式下的) |
\一个 | 字符串开头(忽略模式) |
\z | 字符串结尾(忽略模式) |
\Z | 行尾或字符串 |
\G | 搜索开始的位置 |
\b | 在单词边界上 |
\B | 不在单词边界上 |
(?=expr) | 仅当表达式在右侧匹配时才继续匹配(expr) |
(?!expr) | 仅当表达式在右侧不匹配时才继续匹配(expr) |
(?<=expr) | 仅当左侧的表达式匹配时才继续匹配()expr) |
(?expr) | 仅当左侧的表达式不匹配时才继续匹配()expr) |
(?>expr) | 子表达式匹配一次,不会回溯expr |
分组构造 | |
语法 | 意义 |
(expr) | 将匹配的表达式捕获到索引组中expr |
(?number) | 将匹配的子字符串捕获到指定的组中number |
(?'name') | 将匹配的子字符串捕获到组中name |
(?'name1-name2') | 取消定义并将间隔和当前组存储到 ;如果未定义,则匹配回溯name2name1name2 |
(?:expr) | 非捕获组 |
返回引用 | |
参数语法 | 意义 |
\index | 引用以前捕获的组index |
\k<name> | 引用以前捕获的组name |
交替 | |
表达式语法 | 意义 |
| | 逻辑 |
(?(expr)yes|no) | 如果表达式匹配,则匹配;否则,匹配(可选)yesnono |
(?(name)yes|no) | 如果命名组具有匹配项,则匹配;否则,匹配(可选)yesnono |
杂项构造 | |
表达式语法 | 意义 |
(?#comment) | 内联注释 |
#comment | 注释到行尾(仅适用于忽略模式空白模式) |
正则表达式选项 | |
选择 | 意义 |
(?一) | 不区分大小写的匹配(“忽略”大小写) |
(?m) | 多线模式;更改 ^ 和 $,以便它们匹配任何行的开头和结尾 |
(?n) | 仅捕获显式命名或编号的组 |
(? | 编译为中间语言 |
(? | 单线模式;更改“.”的含义,使其与每个字符匹配 |
(?x) | 从模式中消除未转义的空格 |
(?r) | 从右到左搜索;无法在中游指定 |