【问题标题】:Capture group on (possibly empty) string?在(可能为空)字符串上捕获组?
【发布时间】:2021-07-13 14:17:38
【问题描述】:

如果我有一个字符串并希望捕获一个可能为空的字符串(然后检查它是否确实为空),我该怎么做?当捕获的部分不为空时,我的正则表达式有效,并且在第一印象中,它似乎在字符串为空的情况下有效,因为Console.WriteLine(second); 不产生任何输出。

string notEmpty = "abc|CaptureMeButICanBeEmpty|def";
string isEmpty = "abc||def";
string regEx = @"^\w+\|([^|]+|)\|\w+$";
var match = Regex.Match(notEmpty, regEx);
var match2 = Regex.Match(isEmpty, regEx);

string first = match.Groups[1].Value;
Console.WriteLine(first); // CaptureMeButICanBeEmpty
string second = match2.Groups[1].Value;
Console.WriteLine(second); // No Output

Console.WriteLine(match.Groups.Count); // 2
Console.WriteLine(match2.Groups.Count); // 2

但是,不可能知道second 是否为空因为捕获组为空,或者仅仅是因为根本没有第二个捕获组。 也就是说:

string third = match2.Groups[2].Value;
Console.WriteLine(second); // No Output

也不会产生任何输出,因为捕获组不存在,并且使用类似的东西进行测试

Console.WriteLine(string.IsNullOrEmpty(second));
Console.WriteLine(string.IsNullOrEmpty(third))

两者都返回 true(我预计 string.IsNullOrEmpty(second) 可能会返回 true,但 string.IsNullOrEmpty(third) 会抛出索引越界异常或类似的东西。

我意识到我可以使用match.Groups.Count 的结果来确定示例代码中应该有多少个捕获组 [注意:C# 将 Groups[0] 定义为整个匹配] 但我不想依赖关于这个。

【问题讨论】:

  • 为什么不简单地捕获之前和之后的内容,然后从字符串中删除两者,将结果中间“组”作为字符串?
  • 我认为您需要一个可选组@"^\w+\|([^|]+)?\|\w+$",然后通过访问匹配项的.Success 属性来检查组是否匹配,请参阅this C# demo。当match2.Groups[1].Success 为 false 时,您知道 Group 2 值为空。
  • 你需要正则表达式吗?只使用string.Split 是否可行?
  • @WiktorStribiżew 谢谢 - 我明白你对可选组的意思。认为您打算执行检查以查看 Success 属性是否为真,然后获取值而不是打印布尔值,但我明白了要点。然而,这仍然可以通过首先访问match.Groups.Count 以获取组数并以这种方式索引Groups 属性来完成,这有点像我想避免的。也许没有超级 clean 方法可以做到这一点。 @JonasH 在此示例代码中是的,但实际代码是一个非常长的管道分隔字符串,其中包含许多(可能是空的)捕获组,所以不太漂亮
  • 要知道字符串中是否有捕获组(例如"abcdef"中没有)然后使用@"^\w+(\|[^|]*\|)?\w+$"Groups[1].Success将只有True如果有一个捕获组,无论是否为空。

标签: c# .net regex string


【解决方案1】:

您检查组数的尝试是错误的,因为组数是由(或)正则表达式模式设置的:组数始终是捕获组的数量 + 1(对于整个匹配)。所以,如果你检查match.Groups.Count,它总是会给你2,因为^\w+\|([^|]+|)\|\w+$ 包含一个捕获组。

现在,不可能在强制捕获组中使用可选模式来检查组是否匹配,因为无论如何,组值都将使用空字符串初始化,然后组 .Success 属性将设置为true

这里唯一的出路就是让捕获组模式可选然后检查匹配组Success属性,如果是true,获取组的字符串值,否则,分配您选择的默认值。

这是C# demo

string notEmpty = "abc|CaptureMeButICanBeEmpty|def";
string isEmpty = "abc||def";
string regEx = @"^\w+\|([^|]+)?\|\w+$";
var match = Regex.Match(notEmpty, regEx);
var match2 = Regex.Match(isEmpty, regEx);
 
var first = match.Groups[1].Success ? match.Groups[1].Value : "No ouptut";
Console.WriteLine(first); // => CaptureMeButICanBeEmpty
var second = match2.Groups[1].Success ? match2.Groups[1].Value : "No ouptut";
Console.WriteLine(second.ToString()); // => No Output

好吧,如果您在检查.Success 属性后使用if 块,您实际上可以执行您需要的任何操作。

【讨论】:

  • 谢谢你,这是有道理的,但你所说的 .Sucess 属性对于非可选捕获组(即使字符串为空)始终为真,当我运行代码。我现在意识到为什么我原来的正则表达式总是这样做,但是如果你将([^|]+|) 更改为([^|]+),即使没有成为可选的捕获组,它似乎也可以工作。即this demo code 获得相同的结果。尽管您的解决方案使意图更清晰,并且肯定是更好的实践
  • @amy ^\w+\|([^|]+)\|\w+$abc||def 字符串中找不到任何匹配项。第 1 组是假的,因为整场比赛都失败了。见this C# demo
猜你喜欢
  • 2019-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多