【问题标题】:Highlight Words from a Regex Match突出显示正则表达式匹配中的单词
【发布时间】:2018-10-29 18:31:01
【问题描述】:

我正在尝试使用Regex 在段落中搜索某些文本。我希望现实主义者在前后返回 X 个单词,并在所有出现的文本周围添加高亮显示。

例如: 考虑以下段落。结果前后应至少有 10 个字符,并且没有单词被截断。搜索词是“狗”。

狗是一种宠物。它是最听话的动物之一。那里 世界上有很多种狗。其中一些非常友好 而其中一些是危险的。狗有不同的颜色,比如 黑色、红色、白色和棕色。有些老人的皮肤滑溜溜的 有些皮肤粗糙。狗是肉食性动物。他们喜欢 吃肉。它们有四条腿、两只耳朵和一条尾巴。狗是 受过训练以执行不同的任务。他们保护我们免受小偷 b) 守卫我们的房子。他们是充满爱的动物。狗叫人的 最好的朋友。他们被警察用来寻找隐藏的东西。他们 是世界上最有用的动物之一。狗哥尼特!

我想要的结果是一个如下所示的数组:

  • 是宠物
  • 世界上有很多种
  • 危险。 是不同的
  • 皮肤粗糙。 是肉食性的
  • 还有一条尾巴。 经过训练
  • 动物。 被称为
  • 世界。 gonit!

我有什么:

我四处搜索,发现以下正则表达式完美地返回了所需的结果,但没有添加额外的格式。我创建了几种方法来促进每个功能:

private List<List<string>> Search(string text, string searchTerm, bool searchEntireWord) {
    var result = new List<List<string>>();
    var searchTerms = searchTerm.Split(' ');
        foreach (var word in searchTerms) {
            var searchResults = ExtractParagraph(text, word, sizeOfResult, searchEntireWord);
            result.Add(searchResults);
            if (searchResults.Count > 0) {
                foreach (var searchResult in searchResults) {
                    Response.Write("<strong>Result:</strong> " + searchResult + "<br>");
                }
            }
        }
    return result;
}

private List<string> ExtractParagraph(string text, string searchTerm, sizeOfResult, bool searchEntireWord) {
    var result = new List<string>();
    searchTerm = searchEntireWord ? @"\b" + searchTerm + @"\b" : searchTerm;
    //var expression = @"((^.{0,30}|\w*.{30})\b" + searchTerm + @"\b(.{30}\w*|.{0,30}$))";
    var expression = @"((^.{0," + sizeOfResult + @"}|\w*.{" + sizeOfResult + @"})" + searchTerm + @"(.{" + sizeOfResult + @"}\w*|.{0," + sizeOfResult + @"}$))";
    var wordMatch = new Regex(expression, RegexOptions.IgnoreCase | RegexOptions.Singleline);

    foreach (Match m in wordMatch.Matches(text)) {
        result.Add(m.Value);
    }
    return result;
}

我可以这样称呼它:

var text = "The Dog is a pet animal. It is one of...";
var searchResults = Search(text, "dog", 10);
if (searchResults.Count > 0) {
    foreach (var searchResult in searchResults) {
        foreach (var result in searchResult) {
            Response.Write("<strong>Result:</strong> " + result + "<br>");
        }
    }
}

我还不知道在 10 个字符中多次出现该词的结果或如何处理。即:如果一个句子有“狗当然是狗!”。我想我可以稍后再处理。

测试:

var searchResults = Search(text, "dog", 0, false); // should include only the matched word
var searchResults = Search(text, "dog", 1, false); // should include the matched word and only one word preceding and following the matched word (if any)
var searchResults = Search(text, "dog", 10, false); // should include the matched word and up to 10 characters (but not cutting off words in the middle) preceding and following it (if any)
var searchResults = Search(text, "dog", 50, false); // should include the matched word and up to 50 characters (but not cutting off words in the middle) preceding and following it (if any)

问题:

我创建的函数允许搜索将 searchTerm 仅作为整个单词或单词的一部分来查找。

我所做的是在显示结果时对结果进行简单的Replace(word, "&lt;strong&gt;" + word "&lt;/strong&gt;")。如果我正在搜索单词的一部分,这非常有用。但是在搜索整个单词时,如果结果中包含 searchTerm 作为单词的一部分,则该部分单词会突出显示。

例如:如果我搜索“狗”,结果是:“所有狗都去狗天堂。”突出显示为“所有都去天堂”。但我想要“所有的狗都去天堂。”

问题:

问题是我怎样才能让 matched 单词用一些 HTML 包裹起来,比如 &lt;strong&gt; 或其他任何我想要的东西?

【问题讨论】:

  • 评论不用于扩展讨论;这个对话是moved to chat
  • 我认为您当前的描述需要更新。您想要 1) 提取搜索词与周围文本的所有重叠匹配项,2) 将包含词条的词/搜索词作为整个词(取决于 searchEntireWord 选项)在每个匹配项中使用强标签。正确的?如果是,你可以试试this code
  • 嗨@WiktorStribiżew 实际上,我回顾了您以前的解决方案,尽管如果前一个单词有逗号或句点,它不会在搜索词之前返回字符。我做了进一步的测试,发现情况并非如此。我在聊天中添加了我当前的解决方案。您是否介意查看并根据需要进行任何更改。
  • 我稍微清理了模式并修复了整个单词expression 模式。见this demo。如果它产生预期的结果,请告诉我,我会发布一个答案并解释每件事。
  • @WiktorStribiżew 到目前为止它似乎确实有效。你能解释一下你的这个片段的版本(?&lt;!\w)(dog)(?!\w)与我的(\w*\b)(dog)(\b\w*)有什么不同吗?

标签: c# regex


【解决方案1】:

您的解决方案应该能够做两件主要事情:1) 提取匹配项,即关键字/短语加上围绕它们的额外左右上下文,以及 2) 用标签包装搜索词。

提取正则表达式(例如,左右各 10 个字符)是

(?si)(?<!\S).{0,10}(?<!\S)\S*dog\S*(?!\S).{0,10}(?!\S)

请参阅regex demo

详情

  • (?si) - 启用 SinglelineIgnoreCase 修饰符(. 将匹配所有字符且模式不区分大小写)
  • (?&lt;!\S) - 左侧空白边界
  • .{0,10} - 0 到 10 个字符
  • (?&lt;!\S) - 左侧空白边界
  • \S*dog\S* - dog 周围有任何 0+ 个非空白字符(注意:如果 searchEntireWordfalse,您需要从中删除 \S*图案部分)
  • (?!\S) - 右侧空白边界
  • .{0,10} - 0 到 10 个字符
  • (?!\S) - 右侧空白边界。

在C#中,它会被定义为

var expression = string.Format(@"(?si)(?<!\S).{{0,{0}}}(?<!\S)\S*{1}\S*(?!\S).{{0,{0}}}(?!\S)", sizeOfResult, Regex.Escape(searchTerm)); 
if (searchEntireWord) { 
    expression = string.Format(@"(?si)(?<!\S).{{0,{0}}}(?<!\S){1}(?!\S).{{0,{0}}}(?!\S)", sizeOfResult, Regex.Escape(searchTerm)); 
} 

请注意,{{ 实际上是一个文字 {}} 是格式化字符串中的文字 }

第二个用强标签包裹关键术语的正则表达式要简单得多:

Regex.Replace(x.Value, 
            searchEntireWord ? 
                string.Format(@"(?i)(?<!\S){0}(?!\S)", Regex.Escape(searchTerm)) : 
                string.Format(@"(?i){0}", Regex.Escape(searchTerm)), 
            "<strong>$&</strong>")

请注意,替换模式中的$&amp; 指的是整个匹配值。

C#代码:

public static List<string> ExtractTexts(string text, string searchTerm, int sizeOfResult, bool searchEntireWord) 
{
    var expression = string.Format(@"(?si)(?<!\S).{{0,{0}}}(?<!\S)\S*{1}\S*(?!\S).{{0,{0}}}(?!\S)", sizeOfResult, Regex.Escape(searchTerm)); 
    if (searchEntireWord) { 
        expression = string.Format(@"(?si)(?<!\S).{{0,{0}}}(?<!\S){1}(?!\S).{{0,{0}}}(?!\S)", sizeOfResult, Regex.Escape(searchTerm)); 
    } 
    return Regex.Matches(text, expression) 
        .Cast<Match>() 
        .Select(x => Regex.Replace(x.Value, 
            searchEntireWord ? 
                string.Format(@"(?i)(?<!\S){0}(?!\S)", Regex.Escape(searchTerm)) : 
                string.Format(@"(?i){0}", Regex.Escape(searchTerm)), 
            "<strong>$&</strong>"))
        .ToList();
}

Sample usage (see demo):

var text = "The Dog is a real-pet animal. There's an undogging dog that only undogs non-dogs. It is one of the most obedient animals. There are many kinds of dogs in the world. Some of the are very friendly while some of them a dangerous. Dogs are of different color like black, red, white and brown. Some old them have slippery shiny skin and some have rough skin. Dogs are carnivorous animals. They like eating meat. They have four legs, two ears and a tail. Dogs are trained to perform different tasks. They protect us from thieves b) guarding our house. They are loving animals. A dog is called man's best friend. They are used by the police to find hidden things. They are one of the most useful animals in the world. Doggonit!";
var searchTerm = "dog";
var searchEntireWord = false;
Console.WriteLine("======= 10 ========");
var results = ExtractTexts(text, searchTerm, 10, searchEntireWord);
foreach (var result in results)
    Console.WriteLine(result);

输出:

======= 10 ========
(?si)(?<!\S).{0,10}(?<!\S)\S*dog\S*(?!\S).{0,10}(?!\S)
The <strong>Dog</strong> is a
an un<strong>dog</strong>ging <strong>dog</strong> that
only un<strong>dog</strong>s non-<strong>dog</strong>s.
kinds of <strong>dog</strong>s in the
<strong>Dog</strong>s are of
skin. <strong>Dog</strong>s are
a tail. <strong>Dog</strong>s are
A <strong>dog</strong> is called
world. <strong>Dog</strong>gonit!

另一个例子:

Console.WriteLine("======= 15 ========");
results = ExtractTexts(text, searchTerm, 15, searchEntireWord);
foreach (var result in results)
    Console.WriteLine(result);

输出:

======= 15 ========
(?si)(?<!\S).{0,15}(?<!\S)\S*dog\S*(?!\S).{0,15}(?!\S)
The <strong>Dog</strong> is a real-pet
There's an un<strong>dog</strong>ging <strong>dog</strong> that only
un<strong>dog</strong>s non-<strong>dog</strong>s. It is one of
many kinds of <strong>dog</strong>s in the world.
a dangerous. <strong>Dog</strong>s are of
rough skin. <strong>Dog</strong>s are
and a tail. <strong>Dog</strong>s are trained to
animals. A <strong>dog</strong> is called
in the world. <strong>Dog</strong>gonit!

【讨论】:

    【解决方案2】:

    使用Regex.Replace的简单解决方案:

    public bool HighlightExactMatchOnly(string input, string textToHighlight, string expected)
    {
        // given
        var escapedHighlight = Regex.Escape(textToHighlight);
    
        // when
        var result = Regex.Replace(input, @"\b" + escapedHighlight + @"\b", "<strong>$0</strong>");
    
        return expected == result;
    }
    

    测试:

    var text = "My test dogs with a single dog and some text behind";
    var expected = "My test dogs with a single <strong>dog</strong> and some text behind";
    HighlightExactMatchOnly(text , "dog", expected);
    

    请注意,这不是最快的解决方案。

    【讨论】:

    • 谢谢!我正在尝试这个,但不幸的是,我们仍在运行 C#5,我可以尝试剖析它以在我们的解决方案中工作。如果我得到它,我会回复你,除非你能在我想出来之前提供一个。 =)
    • 所以我想通了(删除“$”并确保可行项是正确的。但我没有得到结果。我会检查原因。
    • 但是请注意@Wiktor 提供的ideone.com/R6iMNO 比这个例子做得更多,可能是更好的解决方案。 (此处:仅突出显示,Wiktor 的示例摘录和突出显示)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 2021-01-24
    • 2013-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多