【问题标题】:Regex to encapsulate any non-matched sections正则表达式封装任何不匹配的部分
【发布时间】:2019-04-22 11:55:47
【问题描述】:

使用示例短语:

所有男人都喜欢widgets,但有些男人比其他人更喜欢widgets

该示例包含单词 widgets 的两种用法。我想做的是匹配外面的位。请注意,虽然小部件是一个词,但我正在寻找一种也适用于多个词的正则表达式解决方案。

鉴于上述示例,输出将包含 3 个匹配项:

  1. 男人都喜欢
  2. 但有些男人喜欢
  3. 比别人多

到目前为止,我有以下仅部分有效的正则表达式:

@"(?!widgets\b)\b\w+"

限制是它不能处理多个单词短语 - 它还匹配每个否定词,而不是 /succeed 之前的整个词串,所以“all”、“men”和“like”而不是“all男人喜欢”。

我有另一个解决方案,它涉及一个更简单的正则表达式和一些额外的 c# 代码中的斩波和更改,但这不是我满意的解决方案:

string EmboldenString(string text, string termToExclude)
{
    var pattern = $@" ?{termToExclude} ?";

    var tagStart = "<b>";
    var tagEnd = "</b>";

    var result = Regex.Replace($"{tagStart}{text}{tagEnd}", pattern, (match) =>
    {
        return $"{tagEnd}{match}{tagStart}";
    });

    var emptyTag = tagStart + tagEnd;

    if (result.StartsWith(emptyTag))
        result = result.TrimStart(emptyTag.ToCharArray());

    if (result.EndsWith(emptyTag))
        result = result.TrimEnd(emptyTag.ToCharArray());

    return result;
}

从上面的代码可以看出,目标是给所有不匹配的位添加标签。

任何人都可以帮助更好的正则表达式。

【问题讨论】:

  • 请改用Regex.Split()。例如,\b(?:widgets|foo|bar)\b.
  • 在上面的代码中,我在匹配项周围添加了 标记,但返回了整个字符串 - 如果我使用 Regex.Split,怎么可能做到这一点?

标签: c# regex


【解决方案1】:

您可以使用Regex.Split,然后使用您喜欢的任何标签将结果数组/列表中的每个奇数元素包装起来:

var s = "All men like widgets but some men like widgets more than others";
var chunks = Regex.Split(s, @"(\s*\bwidgets\b\s*)");
var result = string.Concat(chunks.Select((i, index) => index % 2 == 0 ? $"<b>{i}</b>" : i));
Console.WriteLine(result);
// => <b>All men like</b> widgets <b>but some men like</b> widgets <b>more than others</b>

请参阅C# demo

(\s*\bwidgets\b\s*) 模式匹配并捕获到第 1 组 0+ 个空格、整个单词 widgets 和 0+ 个空格。这些匹配被保存在结果数组中,因为它们被捕获。每个奇数元素都是一个不匹配的子字符串。

如果您不想用标签包装空白的不匹配项,请添加额外的!string.IsNullOrWhiteSpace(i) 检查:

var chunks = Regex.Split(s, @"(\s*\bwidgets\b\s*)");
var result = string.Concat(
    chunks.Select((i, index) => 
        index % 2 == 0 && !string.IsNullOrWhiteSpace(i) ? 
            $"<b>{i}</b>" : i));

this C# demo

【讨论】:

  • 神奇的代码 Wiktor - 但它并不适用于所有情况!如果单词 'widgets' 放在开头或结尾,而不是中间,则会添加一组额外的空标签。
  • @JohnOhara 这是正确的:会有空字符串。当您渲染&lt;b&gt;&lt;/b&gt; 时,最终用户不会在这里看到任何问题。如果需要,您可以将它们全部删除作为后处理步骤。 result = result.Replace("&lt;b&gt;&lt;/b&gt;", "");
  • @JohnOhara 此外,您不能在 .NET 正则表达式中编写不匹配某些任意多字符字符串的正则表达式。 Regex.Split 是最简单的解决方案。
  • @JohnOhara 请同时检查ideone.com/llY3gi。如果您不使用标签包装空/空白项目,它似乎可以按您的需要工作。
  • 谢谢 - 太好了。
【解决方案2】:

按照 Ahmed 的建议,您应该使用 Regex.Spilt()

这是您的用例示例

var op = Regex.Split("All men like widgets but some men like widgets more than others", @"widgets");

输出

//All men like
//but some men like
//more than others

更新

你可以使用下面的代码

string op = GetTagsAroud("All men like widgets but some men like widgets more than others", @"widgets");

方法

static string GetTagsAroud(string input, string splitText)
{
    var matches = Regex.Split(input, splitText);
    StringBuilder output = new StringBuilder();
    foreach (string match in matches)
    {
        output.Append("<b>");
        output.Append(match.Trim());
        output.Append("</b>");
    }
    return output.ToString();
}

【讨论】:

  • 感谢您的回复。我只是想弄清楚如何在输出周围添加 标记。 Regex.Split 上没有委托,并且无法更新 ForEach 的内容。
  • 所以你想在每个字符串周围添加 标签?喜欢 所有男人都喜欢 但有些男人喜欢 ?
  • 没错,但修改后我需要返回整个原始字符串:All men like widgets but some men like widgets 比其他人更多
  • 感谢您的意见 - 虽然不太奏效。现在接受了另一个答案,但您可能想检查一下您的代码以满足您自己的好奇心。
猜你喜欢
  • 1970-01-01
  • 2011-06-13
  • 1970-01-01
  • 2017-11-29
  • 2017-06-23
  • 1970-01-01
  • 2015-04-12
  • 1970-01-01
相关资源
最近更新 更多