《c#10 in a nutshell》--- 读书随记(14)

Chapter 25. Regular Expressions

内容来自书籍《C# 10 in a Nutshell》
Author:Joseph Albahari
需要该电子书的小伙伴,可以留下邮箱,有空看到就会发送的

正则表达式语言识别字符模式。支持正则表达式的 .NET 类型基于 Perl 5正则表达式,并支持搜索和搜索/替换功能

正则表达式用于以下任务:

  • 验证文本输入,如密码和电话号码
  • 将文本数据解析为更结构化的表单
  • 替换文档中的文本模式

所有的正则表达式类型都定义在System.Text.RegularExpressions命名空间

Regular Expression Basics

最常见的正则表达式运算符之一是量词。?是与前一项0或1次匹配的量词。换句话说?意味着可以选择。项目可以是单个字符,也可以是方括号中的复杂字符结构

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

默认情况下,正则表达式引擎从左向右工作,因此只返回最左边的匹配。可以使用 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

Matches 方法返回所有匹配项在一个数组中

foreach (Match m in Regex.Matches("One color? There are two colours in my head!", @"colou?rs?"))
    Console.WriteLine (m);

Compiled Regular Expressions

在前面的一些示例中,我们使用相同的模式重复调用静态 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)来动态构建和编译适合该特定正则表达式的代码。这将导致更快的匹配,代价是初始编译成本。

RegexOptions 标志枚举允许您调整匹配行为。RegexOptions 的一个常见用途是执行不区分大小写的搜索

Console.WriteLine (Regex.Match ("a", "A", RegexOptions.IgnoreCase)); // a

这将应用当前区域性的大小写等价规则。使用 CultureInvariable 标志可以请求不变区域性

Console.WriteLine (Regex.Match ("a", "A", RegexOptions.IgnoreCase
                                        | RegexOptions.CultureInvariant));

Character Escapes

正则表达式有一些元字符,用于特殊功能:

\ * + ? | { [ () ^ $ . #

要从字面上使用元字符,必须在字符前加上反斜杠或转义

Console.WriteLine (Regex.Match ("what?", @"what\?")); // what? (correct)

Character Sets

Quantifiers

Greedy Versus Lazy Quantifiers

量词默认是贪婪的,贪婪的量词尽可能多地重复匹配;懒惰是尽可能少地匹配,需要懒惰,可以给量词加上 ? 后缀

Zero-Width Assertions

正则表达式语言允许您通过后视(lookbehind)、前视(lookahead)、锚(anchors)和单词边界(word boundaries),对匹配之前或之后应该发生的事情设置条件。这些称为零宽度断言,因为它们不会增加匹配本身的宽度(或长度)。

Lookahead and Lookbehind

(?=expr) 构造检查后面的文本是否与 expr 匹配,而不在结果中包含 expr。这就是所谓的 positive lookahead

Console.WriteLine (Regex.Match ("say 25 miles more", @"\d+\s(?=miles)")); // 25

注意,结果中没有返回单词“ miles”,即使需要它来满足匹配

与上面相反的是 negative lookahead ,(?!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)是一个 positive lookbehind,表示在匹配之前需要满足条件

(?<!expr)是和上面的相反 negative lookbehind,要求匹配之前不能有指定的表达式

Anchors

^ 匹配字符串的开始;$ 匹配字符串的结尾

Word Boundaries

匹配单词的边界,是匹配位置,而不会被捕获,就是是^$一样

Groups

有时,将正则表达式分隔成一系列子表达式或组是有用的,比如,匹配电话\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

组是正则表达式语言本身的一部分。这意味着可以在正则表达式中引用组。\n 语法允许您在表达式中按组号 n 对组进行索引。

foreach (Match m in Regex.Matches ("pop pope peep", @"\b(\w)\w+\1\b"))
    Console.Write (m + " "); // pop peep

Named Groups

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)或者(?<group-name>group-expr)

有两种使用形式:
\k'group-name'或者\k<group-name>

Replacing and Splitting Text

string find = @"\bcat\b";
string replace = "dog";
Console.WriteLine (Regex.Replace ("catapult the cat", find, replace));

替换字符串可以使用 $0 替换构造引用原始匹配。下面的示例将字符串中的数字包装在尖括号中

string text = "10 plus 20 makes 30";
Console.WriteLine (Regex.Replace (text, @"\d+", @"<$0>"));

// OUTPUT: <10> plus <20> makes <30>

也可以用$1, $2, $3或者${name}来引用匹配的组

MatchEvaluator Delegate

Replace 有一个重载,该重载接受每个匹配调用的 Matchevalator 委托。这允许您在正则表达式语言表达能力不足时,将替换字符串的内容委托给 C # 代码

Console.WriteLine (Regex.Replace ("5 is less than 10", @"\d+",
    m => (int.Parse (m.Value) * 10).ToString()) );
// OUTPUT: 50 is less than 100

Splitting Text

foreach (string s in Regex.Split ("a5b7c", @"\d"))
    Console.Write (s + " ");    // a b c
posted @ 2022-07-02 22:08  huang1993  阅读(138)  评论(3)    收藏  举报