【问题标题】: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]+)

但它只返回带有小提琴乐器的那个。非常感谢您对此的见解。

【问题讨论】:

  • ID: JK546||Violin: 0|Expiry: Aug14,20201 - | 之前的两个Violin,是不是搞错了?
  • 有效期为 18180 年后。经久耐用! ;)
  • @AKSingh 已经修复了它
  • @Wyck lol 看起来我还是个半生不熟的调试器

标签: c# regex string


【解决方案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# 是少数可以处理可变长度查找的语言之一。

        【讨论】:

          猜你喜欢
          • 2011-02-23
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-24
          • 1970-01-01
          相关资源
          最近更新 更多