RegEx - 重用子表达式

时间:2011-11-20 19:51:35

标签: c# .net regex

假设我有一个匹配十六进制32位数的正则表达式:

([0-9a-fA-F]{1,8})

当我构造一个需要多次匹配的正则表达式时,例如

(?<from>[0-9a-fA-F]{1,8})\s*:\s*(?<to>[0-9a-fA-F]{1,8})

我是否每次都必须重复子表达式定义,或者有没有办法“命名和重用”它?

我想象的是(警告,发明语法!

(?<from>{hexnum=[0-9a-fA-F]{1,8}})\s*:\s*(?<to>{=hexnum})

其中hexnum=将定义子表达式“hexnum”,而{= hexnum}将重用它。

因为我已经了解它很重要:我正在使用.NET的System.Text.RegularExpressions.Regex,但一般的答案也很有趣。

6 个答案:

答案 0 :(得分:9)

RegEx子程序

如果要多次使用子表达式而不重写它,可以对其进行分组,然后将其称为子例程。子程序可以通过名称,索引或相对位置来调用。

PCRE,Perl,Ruby,PHP,Delphi,R等支持子程序。遗憾的是,缺少.NET Framework,但是您可以使用一些用于.NET的PCRE库(例如https://github.com/ltrzesniewski/pcre-net)。

语法

以下是子程序的工作原理:假设你有一个你想连续重复三次的子表达式model

标准RegEx
任何:[abc]

子程序,按名称
Perl:[abc][abc][abc]
PCRE:(?'name'[abc])(?&name)(?&name)
Ruby:(?P<name>[abc])(?P>name)(?P>name)

子程序,按索引
Perl / PCRE:(?<name>[abc])\g<name>\g<name>
Ruby:([abc])(?1)(?1)

子程序,按相对位置
Perl:([abc])\g<1>\g<1>
PCRE:([abc])(?-1)(?-1)
Ruby:([abc])(?-1)(?-1)

子程序,预定义
这定义了一个子程序而不执行它 Perl / PCRE:([abc])\g<-1>\g<-1>

实施例

匹配有效的IPv4地址字符串,从0.0.0.0到255.255.255.255:
(?(DEFINE)(?'name'[abc]))(?P>name)(?P>name)(?P>name)

没有子程序:
((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.(?1)\.(?1)\.(?1)

并解决原贴问题:
((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))\.((?:25[0-5])|(?:2[0-4][0-9])|(?:[0-1]?[0-9]?[0-9]))

更多信息

http://regular-expressions.info/subroutine.html
http://regex101.com/

答案 1 :(得分:4)

为什么不做这样的事情,不是更短,而是更容易维护。

String.Format("(?<from>{0})\s*:\s*(?<to>{0})", "[0-9a-zA-Z]{1,8}");

如果你想要更多的自我记录代码,我会将数字正则表达式字符串分配给一个正确命名的const变量。

答案 2 :(得分:3)

如果我正确理解你的问题,你想重用某些模式来构建一个更大的模式吗?

string f = @"fc\d+/";
string e = @"\d+";
Regex regexObj = new Regex(f+e);

除此之外,只有在尝试匹配之前在正则表达式中匹配的完全相同的字符串时,使用backreferences才有用。

e.g。

/\b([a-z])\w+\1\b/

仅与上述文字中的textspaces匹配:

这是一个示例文本,它不是标题,因为它不以2个空格结尾。

答案 3 :(得分:2)

.NET regex不支持模式递归,如果你可以在Ruby和PHP / PCRE中使用(?<from>(?<hex>[0-9a-fA-F]{1,8}))\s*:\s*(?<to>(\g<hex>))(其中hex是一个&#34;技术&#34;命名的捕获组,其名称不应该出现在主模式中),在.NET中,您可以将块定义为单独的变量,然后使用它们来构建动态模式。

从C#6开始,您可以使用内插的字符串文字,它看起来非常像PCRE / Onigmo子模式递归,但实际上更清晰,并且当组的名称与&#34;技术&#相同时没有潜在的瓶颈#34;捕获组:

C# demo

using System;
using System.Text.RegularExpressions;

public class Test
{
    public static void Main()
    {
        var block = "[0-9a-fA-F]{1,8}";
        var pattern = $@"(?<from>{block})\s*:\s*(?<to>{block})";
        Console.WriteLine(Regex.IsMatch("12345678  :87654321", pattern));
    }
}

$@"..." verbatim 插值字符串文字,其中转义序列被视为文字反斜杠和后面的字符的组合。确保将文字{{{}}}定义(例如$@"(?:{block}){{5}}"以重复block 5次)。

对于较旧的C#版本,请使用string.Format

var pattern = string.Format(@"(?<from>{0})\s*:\s*(?<to>{0})", block);

Mattias's answer中建议。

答案 4 :(得分:1)

没有这样的预定义类。我认为你可以使用ignore-case选项简化它,例如:

(?i)(?<from>[0-9a-z]{1,8})\s*:\s*(?<to>[0-9a-z]{1,8})

答案 5 :(得分:-2)

要重用名为capture group的正则表达式,请使用以下语法:\ k&lt; name&gt;或\ k'name'

所以答案是:

(?<from>[0-9a-fA-F]{1,8})\s*:\s*\k<from>

更多信息:http://www.regular-expressions.info/named.html