【问题标题】:C# Regular Expression: How to extract a collectionC# 正则表达式:如何提取集合
【发布时间】:2011-11-28 08:48:14
【问题描述】:

我在文本文件中有收藏:

(Collection
  (Item "Name1" 1 2 3)
  (Item "Simple name2" 1 2 3)
  (Item "Just name 3" 4 5 6))

集合也可以为空:

(Collection)

项目数未定义。它可以是一件或一百件。通过之前的提取,我在 Collection 元素之间已经有了内部文本:

(Item "Name1" 1 2 3)(Item "Simple name2" 1 2 3)(Item "Just name 3" 4 5 6)

如果是空集合,它将是空字符串。

如何使用 .Net 正则表达式解析这个集合?

我试过了:

string pattern = @"(\(Item\s""(?<Name>.*)""\s(?<Type>.*)\s(?<Length>.*)\s(?<Number>.*))*";

但是上面的代码并没有产生任何实际的结果。

更新:

我尝试以不同的方式使用正则表达式:

foreach (Match match in Regex.Matches(document, pattern, RegexOptions.Singleline))
{
    for (int i = 0; i < match.Groups["Name"].Captures.Count; i++)
    {
        Console.WriteLine(match.Groups["Name"].Captures[i].Value);
    }
}

while (m.Success)
{
    m.Groups["Name"].Value.Dump();
    m.NextMatch();
}

【问题讨论】:

  • @Michele Virgilio:在输出中我想提取所有项目,例如名称、类型、长度和数字
  • 所以你只需要解析 (Item "Name1" 1 2 3)(Item "Simple name2" 1 2 3)(Item "Just name 3" 4 5 6) 而不是整个字符串跨度>
  • 我认为离开多行解析会更好

标签: c# .net regex c#-4.0


【解决方案1】:

试试

\(Item (?<part1>\".*?\")\s(?<part2>\d+)\s(?<part3>\d+)\s(?<part4>\d+)\)

这将创建一个匹配集合:

Regex regex = new Regex(
      "\\(Item (?<part1>\\\".*?\\\")\\s(?<part2>\\d+)\\s(?<part3>\\d"+
      "+)\\s(?<part4>\\d+)\\)",
    RegexOptions.Multiline | RegexOptions.Compiled
    );

//Capture all Matches in the InputText
MatchCollection ms = regex.Matches(InputText);


//Get the names of all the named and numbered capture groups
string[] GroupNames = regex.GetGroupNames();

// Get the numbers of all the named and numbered capture groups
int[] GroupNumbers = regex.GetGroupNumbers();

【讨论】:

  • 我不知道这是否可行,但总是使用最小捕获(例如 \d+ 总是比像 .* 这样的大规模贪婪匹配更好,因为这会很高兴地匹配你所有的分隔符和其他任何东西有一半的机会。我什至希望看到第一个 .* 被替换为更具体的内容,尽管我们不知道目前那里有效。
  • 实际上贪婪匹配只在 " 分隔符之间使用,它可以包含任何文本。
  • 问题是贪婪匹配将匹配那些分隔符给一半的机会。例如,当您有两个项目时,它将匹配Name1" 1 2 3)(Item "Simple name2" 1 2 3)(Item "Just name 3。使用 [^"]* 会比 .* 更好,以防止它与您的分隔符匹配。当然,引号内的引号可能有一些转义机制,在这种情况下,由于其他原因,这会失败...
  • 是的,但这在这种情况下不起作用:(Item "Name1" 1 2 3) (Item "Simple \" name2" 1 2 3) (Item "Just name 3" 4 5 6 ) 项目 2 根本不匹配
  • 嗯,这就是我最后一句话所说的。由于这个原因,当前没有完全指定输入字符串。使用非贪婪匹配(。*?)可能会起作用,因为它会咬掉 \" 以使其余的模式匹配。如果我们知道名称总是字母数字和空格,我会更喜欢它或类似的东西,而不必担心转义引号之类的原因与您使用 \d+ 而不仅仅是 .+
【解决方案2】:

我认为你可能需要让你的捕获不贪婪......

(?<Name>.*?)

而不是

(?<Name>.*)

【讨论】:

  • 在这种情况下只捕获第一个项目
  • @JohnKZ:你是如何使用正则表达式的?让它不贪婪的全部意义在于让它只匹配一个项目,而不是假设第一个项目的名称是 Name1" 1 2 3)(Item "Simple name2" 1 2 3)(Item "Just name 3 或同样破碎的东西。
【解决方案3】:

我认为你应该读取文件而不是使用 Sting.Split 函数来拆分集合并开始读取它

   String s = "(Collection
              (Item "Name1" 1 2 3)
              (Item "Simple name2" 1 2 3)
              (Item "Just name 3" 4 5 6))";

   string colection[] = s.Split('(');
   if(colection.Length>1)
   {
      for(i=1;i<colection.Length;i++)
      {
          //process string one by one and add ( if you need it
          //from the last item remove )
      }
   }

这将轻松解决问题,无需额外增加规则表达式的负担。

【讨论】:

  • 我同意这一点。我假设当你得到你的字符串时,你必须已经在做类似于上面的事情了。在那个阶段,我只是解析每个项目,而不是将其放入正则表达式的字符串中。如果没有别的,在较小的字符串上进行正则表达式会使生活变得更轻松。 :)
  • 如果他在其中一个字符串中有一个括号,这将非常失败。
  • @Ilia Jerebtsov - 代码是根据 op 给出的字符串生成的。我同意如果有 ( 出现在字符串中,它将失败
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 2015-09-04
  • 1970-01-01
  • 1970-01-01
  • 2017-01-24
  • 2021-07-06
相关资源
最近更新 更多