【问题标题】:Iterative regex capturing in C#C# 中的迭代正则表达式捕获
【发布时间】:2010-10-13 20:13:55
【问题描述】:

我必须读入一个包含多个坐标的文件。该文件的结构如下:

X1/Y1,X2/Y2,X3/Y3,X4/Y4

其中 X 和 Y 是正整数。为了解决这个问题,我想使用一个正则表达式(我认为这通常是一个好主意,因为模式改变时重构最少)。

因此我开发了以下正则表达式:

Regex r = new Regex(@^(?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+))(,(?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+)))*$");

但是,当我在数据上测试这个正则表达式时,例如:

1302/1425,1917/2010

Regex 似乎只记得最后一个 X、Y 和 Coor 组。在这种情况下,Coor 是“12/17”,X 是“1917”,Y 是“2010”。有没有办法生成某种树。所以我找到了一个对象,它给了我所有的 Coor 表达式,每个 Coor 下都有一个 X 和 Y 分量?

如果可能,我只想使用一个正则表达式,因为格式可能会更改为另一种。

【问题讨论】:

  • 编辑:我还需要验证正则表达式,使用 Regex.Matches 不会验证字符串。
  • 你知道预计格式会发生什么样的变化吗?
  • 这没有递归。您可能是指迭代。
  • @CommuSoft:将验证与提取值分开进行而不是同时尝试两者会更简单。你能做到吗?
  • 我想我确实可以先验证完整的正则表达式,然后使用 Matches 方法从每个片段中提取数据。但是,我也对解决此类问题的一般方法感兴趣。例如,如果逗号会(我认为不会)被更复杂的结构替换,那么可以捕获错误匹配。有没有一种通用的方法来构建这样的树。

标签: c# regex recursion capturing-group


【解决方案1】:

您可以使用string.Splitint.Parse 轻松解决这个问题,无需任何正则表达式:

var coords = s.Split(',')
    .Select(x => x.Split('/'))
    .Select(a => new {
        X = int.Parse(a[0]),
        Y = int.Parse(a[1])
    });

如果你想使用正则表达式来验证字符串,你可以这样做:

"^(?!,)(?:(?:^|,)[0-9]+/[0-9]+)*$"

如果您也想使用基于正则表达式的方法提取数据,您可以先使用上述正则表达式验证字符串,然后按如下方式提取数据:

var coords = Regex.Matches(s, "([0-9]+)/([0-9]+)")
    .Cast<Match>()
    .Select(match => new
    {
        X = int.Parse(match.Groups[1].Value),
        Y = int.Parse(match.Groups[2].Value)
    });

如果您真的想使用单个正则表达式同时执行验证和数据提取,您可以使用两个捕获组并在每个组的Captures 属性中找到结果。这是您可以使用单个正则表达式执行验证和数据提取的一种方法:

List<Group> groups =
    Regex.Matches(s, "^(?!,)(?:(?:^|,)([0-9]+)/([0-9]+))*$")
         .Cast<Match>().First()
         .Groups.Cast<Group>().Skip(1)
         .ToList();

var coords = Enumerable.Range(0, groups[0].Captures.Count)
    .Select(i => new
    {
        X = int.Parse(groups[0].Captures[i]),
        Y = int.Parse(groups[1].Captures[i])
    });

但是,您可能需要考虑与基于 string.Split 的解决方案相比,此解决方案的复杂性是否值得。

【讨论】:

    【解决方案2】:

    没有理由对这种简单的格式使用正则表达式。

    只需拆分字符串并使用纯字符串操作即可获取坐标:

    var coordinates =
      fileContent.Split(',').Select(s => {
        int pos = s.IndexOf("/");
        return new {
          X = s.Substring(0, pos),
          Y = s.Substring(pos + 1)
        };
      });
    

    如果文件格式变得更加复杂,您可以将其重构为使用正则表达式。在那之前,像这样的简单代码更容易维护。

    【讨论】:

      【解决方案3】:

      如果您使用“匹配”而不是“匹配”命令,您可能会得到想要的结果。另外,你不能把正则表达式缩短成这样吗:

      Regex(@"((?<Coor>(?<X>[0-9]+)/(?<Y>[0-9]+))|,)*");
      

      【讨论】:

        【解决方案4】:

        我认为你的第一个问题是你的正则表达式有缺陷,锚点正在抛出匹配。这是我想出的:(只是这里显示的正则表达式,没有代码)

        (?&lt;Coor&gt;(?&lt;X&gt;[0-9]+)/(?&lt;Y&gt;[0-9]+))

        Mystagogue 也可以,但会在逗号上产生“空白”匹配项(对我而言)。

        【讨论】:

          猜你喜欢
          • 2015-11-06
          • 2015-08-14
          • 1970-01-01
          • 1970-01-01
          • 2019-10-05
          • 1970-01-01
          • 2015-09-24
          • 2014-01-02
          • 2016-10-09
          相关资源
          最近更新 更多