RegEx – 重用子表达式

假设我有一个匹配hex32位数的正则表达式:

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

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

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

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

我想象一下( 警告,发明语法!

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

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

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

RegEx子程序

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

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

句法

以下是子程序的工作原理:假设你有一个你希望连续重复三次的子表达式[abc]

标准RegEx
任何: [abc][abc][abc]

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

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

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

子程序,预定义
这定义了一个子程序而不执行它。
Perl / PCRE: (?(DEFINE)(?'name'[abc]))(?P>name)(?P>name)(?P>name)

例子

匹配有效的IPv4地址字符串,从0.0.0.0到255.255.255.255:
((?: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]))

并解决原贴问题:
(?(?P[0-9a-fA-F]{1,8}))\s*:\s*(?(?P>hexnum))

更多信息

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

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

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

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

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

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

除此之外,如果您尝试匹配之前在正则表达式中匹配的完全相同的字符串,则使用反向引用只会有所帮助。

例如

 /\b([az])\w+\1\b/ 

只会匹配:上面文字中的textspaces

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

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

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

C#演示 :

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

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

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

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

正如Mattias的答案中所建议的那样。

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

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

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

所以答案是:

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

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