是否有可能有重叠的正则表达式匹配?
Posted
技术标签:
【中文标题】是否有可能有重叠的正则表达式匹配?【英文标题】:Is it possible to have overlapping regex matches? 【发布时间】:2021-08-30 00:28:27 【问题描述】:以这个数据为例:
ID: JK546|Guitar: 0|Piano: 1|Violin: 0|Expiry: Aug14,2021
我想知道是否可以创建一个正则表达式来返回这组匹配项
ID: JK546|Guitar: 0|Expiry: Aug14,2021
ID: JK546|Piano: 1|Expiry: Aug14,2021
ID: JK546|Violin: 0|Expiry: Aug14,2021
我确实尝试在下面创建一个:
ID: (?<id>\w+).*\|(?<instrument>\w+):\s(?<count>\d).*Expiry:\s(?<expiry>[\w\d]+)
但它只返回带有小提琴乐器的那个。非常感谢您对此的见解。
【问题讨论】:
查看这个问题:***.com/questions/8020848/…ID: JK546||Violin: 0|Expiry: Aug14,20201
- |
之前的两个Violin
,是不是搞错了?
有效期为 18180 年后。经久耐用! ;)
@AKSingh 已经修复了它
@Wyck lol 看起来我还是个半生不熟的调试器
【参考方案1】:
我不会使用正则表达式。特别是由于字符串ID: JK546|Guitar: 0|Expiry: Aug14,2021
没有出现在字符串ID: JK546|Guitar: 0|Piano: 1|Violin: 0|Expiry: Aug14,2021
中,所以它并不是严格意义上的匹配,而是更多的替换。但是没有什么好方法可以从所有比赛中获得所有替补。
所以,我只需在|
上拆分输入字符串。
然后你想组成一个由第一个字段、一个中间字段和最后一个字段组成的结果字符串。对于存在的每个中间字段,您将获得一个结果。如果它拆分为 N 个字段,您将获得 N-2 个结果。例如:如果它分成 5 个字段,那么您将得到 3 个结果,每个“中间”字段一个。
string input = "ID: JK546|Guitar: 0|Piano: 1|Violin: 0|Expiry: Aug14,2021";
string[] fields = input.Split('|');
for( int i = 1; i < fields.Length - 1; ++i)
string result = string.Join("|", fields.First(), fields[i], fields.Last());
Console.WriteLine(result);
输出:
编号:JK546|吉他:0|有效期:2021年8月14日 编号:JK546|钢琴:1|有效期:2021年8月14日 编号:JK546|小提琴:0|有效期:2021年8月14日【讨论】:
【参考方案2】:一个正则表达式返回多个匹配多次调用? 我想知道这是否可能。
我不熟悉如何在 C# 中进行正则表达式处理,
但是这个sed
命令会做你想做的事。
也许您可以了解它的工作原理并根据您的需要进行调整:
sed -n ':loop; h; s/^\([^|]*|[^|]*\).*\(|.*\)$/\1\2/p; g; s/^\([^|]*\)|[^|]*\(|.*\)$/\1\2/; t loop'
为简单起见,我们假设输入字符串是“A|B|C|D|E”。
它的作用:
-n
是告诉sed
不要自动打印任何内容的选项
(但只有在被告知时才使用p
命令打印)。
:loop
实际上是“goto”的标签。
所以使用while
循环结构。
h
将模式空间保存到保持空间中。
换句话说,复制你的字符串。
s/^\([^|]*|[^|]*\).*\(|.*\)$/\1\2/p
捕获前两个段
最后一个,并打印结果。
所以“A|B|C|D|E”变成了“A|B|E”(即你想要的第一个输出)。
g
将保存的字符串从保持空间恢复到模式空间。
换句话说,检索您保存的字符串的副本。
s/^\([^|]*\)|[^|]*\(|.*\)$/\1\2/
捕获第一段,
跳过第二个,然后捕获其余部分。
所以“A|B|C|D|E”变成了“A|C|D|E”。
t loop
是“goto”命令。
它说要回到循环的开头
如果最近的替换成功。
换句话说,这是循环的结束,
以及循环条件的说明。
循环的第二次迭代会将“A|C|D|E”更改为“A|C|E”
并打印出来。
然后将“A|C|D|E”更改为“A|D|E”并迭代。
循环的第三次迭代会将“A|D|E”更改为“A|D|E”并打印出来。
(显然没有变化,因为正则表达式中间的.*
匹配“A|D”和“|E”之间的零长度字符串。)
最后的替换将“A|D|E”更改为“A|E”,
然后就什么也找不到了。
【讨论】:
【参考方案3】:您可以利用 .NET Groups.Captures 属性来获取吉他、钢琴和小提琴的值。
(ID: \w+\|)(\w+: \d+\|)+(Expiry: \w+,\d+)
模式匹配:
(ID: \w+\|)
捕获 group 1 匹配 ID:
1+ word chars 和 |
(\w+: \d+\|)+
捕获组 2 重复 1+ 次匹配 1+ 单词字符 :
1+ 数字 |
(Expiry: \w+,\d+)
捕获 第 3 组 匹配 Expiry:
1+ 个单词字符 ,
和 1+ 个数字
查看.NET regex demo | C# demo
例如
var str = "ID: JK546|Guitar: 0|Piano: 1|Violin: 0|Expiry: Aug14,2021";
string pattern = @"(ID: \w+\|)(\w+: \d+\|)+(Expiry: \w+,\d+)";
Match m = Regex.Match(str, pattern);
foreach(Capture c in m.Groups[2].Captures)
Console.WriteLine(m.Groups[1].Value + c.Value + m.Groups[3].Value);
输出
ID: JK546|Guitar: 0|Expiry: Aug14,2021
ID: JK546|Piano: 1|Expiry: Aug14,2021
ID: JK546|Violin: 0|Expiry: Aug14,2021
【讨论】:
【参考方案4】:往后看应该可以:
string foo = @"ID: JK546 | Guitar: 0 | Piano: 1 | Violin: 0 | Expiry: Aug14,2021";
// First look at "Guitar: 0", "Piano: 1" and "Violin: 0". Then look behind "(?<= )" and search for the ID. Then look ahead "(?= )" and search for Expiry.
string pattern = @"(\w+: \d)(?<=(ID: [A-Z0-9]+).*?)(?=.*?(Expiry: \S+))";
foreach (var match in Regex.Matches(foo, pattern))
....
幸运的是,c# 是少数可以处理可变长度查找的语言之一。
【讨论】:
以上是关于是否有可能有重叠的正则表达式匹配?的主要内容,如果未能解决你的问题,请参考以下文章