捕获组之间的 C# 正则表达式空白

C# Regex whitespace between capturing groups

所以基本上,我的输入字符串是某种包含我想要匹配的关键字的文本,前提是:

  1. 每个关键字可能有 whitespace/non-word 个字符 pre/appended,或 none (|\s\W)
  2. 必须恰好有一个 non-word/whtiespace 字符分隔多个关键字,或者关键字位于第 begining/end 行
  3. 仅作为子字符串出现的关键字不算数,例如bar 不匹配 foobarbaz

例如:

input:    "#foo barbazboo tree car"
keywords: {"foo", "bar", "baz", "boo", "tree", "car"}

我正在使用可枚举的关键字和字符串生成器在 C# 中动态生成正则表达式

StringBuilder sb = new();
foreach (var kwd in keywords)
{
   sb.Append($"((|[\s\W]){kwd}([\s\W]|))|");
}
sb.Remove(sb.Length - 1, 1); // last '|'
_regex = new Regex(sb.ToString(), RegexOptions.Compiled | RegexOptions.IgnoreCase);

regexr.com 上测试此模式,给定的输入匹配所有关键字。但是,我不想包含 {bar, baz, boo},因为每个关键字之间没有空格。 理想情况下,我希望我的正则表达式只匹配 {foo, tree, car}.

(( |[\s\W])kwd([\s\W]| )) 这样修改我的模式会导致 {bar, baz, boo} 不被包含,但会在 {tree, car} 上产生伪造,因为对于这种情况,关键字之间必须至少有两个空格。

如何指定“可能只有一个空格分隔两个关键字”,或者换句话说,“半个空格就可以”,保留动态创建正则表达式的能力?

对于您的情况,您需要构建

var pattern = $@"\b(?:{string.Join("|", keywords.OrderByDescending(x => x.Length).Select(Regex.Escape))})\b";
_regex = new Regex(pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

在这里,您会在较短的关键字之前获得较长的关键字,因此,如果您有 foobarfoo bar,则模式将类似于 \b(?:foo\ bar|foo|bar)\b并将匹配 foo bar,而不是 foobar 一旦有这样的匹配。

如果您的关键字看起来像 keywords: {"$foo", "^bar^", "[baz]", "(boo)", "tree+", "+car"},即它们可以在关键字的 start/end 处有特殊字符,您可以使用

_regex = new Regex($@"(?!\B\w)(?:{string.Join("|", keywords.Select(Regex.Escape))})(?<!\w\B)", RegexOptions.Compiled | RegexOptions.IgnoreCase);

$@"(?!\B\w)(?:{string.Join("|", keywords.OrderByDescending(x => x.Length).Select(Regex.Escape))})(?<!\w\B)" 是一个内插的逐字字符串文字,其中包含

  • (?!\B\w) - left-hand
  • (?: - non-capturing 组的开始:
    • {string.Join("|", keywords.OrderByDescending(x => x.Length).Select(Regex.Escape))} - 按长度降序排列关键字,将它们转义并加入 |
  • ) - 小组结束
  • (?<!\w\B) - right-hand自适应动态字边界。