假设我有一个匹配十六进制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
,但一般的答案也很有趣。
答案 0 :(得分:9)
如果要多次使用子表达式而不重写它,可以对其进行分组,然后将其称为子例程。子程序可以通过名称,索引或相对位置来调用。
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/
仅与上述文字中的text
,spaces
匹配:
这是一个示例文本,它不是标题,因为它不以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;捕获组:
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>