使用find/replace和regex将关键字替换为字符串中的url

本文关键字:替换 字符串 url 关键字 find replace regex 使用 | 更新日期: 2023-09-27 17:54:28

我有一个关键字列表(单个单词或几个单词),我想用一些url替换。

:

  • London将被<a href="http://www.mysite/london-events/london">London</a>取代

  • <a href="http://www.mysite/footbal-events/london"> Football events in London</a>在伦敦的足球赛事

  • 伦敦足球赛事与<a href="http://www.mysite/footbal-events/london"> London football events</a>

  • 伦敦足球赛事与<a href="http://www.mysite/footbal-events/london"> Football events London</a>

  • Party sites in london with <a href="http://www.mysite/party-sites/london"> party sites in London</a>

  • London party sites with <a href="http://www.mysite/party-sites/london"> London party sites</a>

我把上面的键/值放在字典中,关键字放在键中,url放在值中,并替换为

内容如下:

伦敦是一个伟大的城市伦敦的足球赛事除了派对伦敦的网站也不错。伦敦足球赛事也很棒伦敦派对网站。享受伦敦!

替换键/值的代码:

private static string ParsedContents(some arguments list here...)
{
    Dictionary<string, string> keyWords = GetKeywordsAndEntityWithURL(some arguments list here...);
    StringBuilder parsedContents = new StringBuilder(contents);
    foreach (var keyWord in keyWords)
    {
        string replacedString = Regex.Replace(parsedContents.ToString(), "''b" + keyWord.Key + "''b", keyWord.Value, RegexOptions.IgnoreCase);
        parsedContents.Remove(0, parsedContents.Length);
        parsedContents.Append(replacedString);
    }
    // retrun parsed contents as string.
    return parsedContents.ToString();
}

当我运行我的代码只有'London'替换为'<a href="http://www.mysite/london-events/london">London</a>'和所有其他只是保持不变,但如果我从关键字中删除'London',它工作得很好。

你能告诉我如何匹配整个字符串吗?

要替换的内容和url是假的:

谢谢

使用find/replace和regex将关键字替换为字符串中的url

因为你想链接的一些短语包含你想链接的其他短语,而链接本身也会包含这些短语,你必须分两个阶段做,如果你想避免棘手的正则表达式:

第1阶段:将每个短语替换为一个不匹配任何其他内容的唯一ID:

  • 您将需要更换更长时间短语要在较短的阶段之前做出当然不能只替换a的一部分短语(如。"伦敦"中的"伦敦"足球事件")。
  • 您可以将要链接的短语和url存储在SortedDictionary中,并提供按长度然后按字母顺序排序字符串的IComparer<string>。请注意,长度相同的字符串仍然作为不同的字符串进行比较,否则您不能将它们同时存储在字典中。
  • 当您替换时每个短语都应该生成链接,将取代它,并建立映射id到链接的字典
  • 如果您使用string.Replace替换你需要的短语处理不同的短语案例作为不同的短语,即。"伦敦的派对场所"则不同从"伦敦派对地点"中找到的需要一个单独的ID。

阶段2:用生成的链接替换所有占位符id。

这里有一个类来做这个:

class TextLinker : IComparer<string>
{
    private SortedDictionary<string, string> phrasesToUrls;
    public TextLinker()
    {
        // Pass self as IComparer to sort dictionary using Compare method.
        phrasesToUrls = new SortedDictionary<string, string>(this);
    }
    public void AddLink(string phrase, string URL)
    {
        phrasesToUrls.Add(phrase, URL);
    }
    public string Link(string text)
    {
        // phase 1: replace phrases to be linked with unique placeholders
        Dictionary<string, string> placeholdersToLinks =
            new Dictionary<string, string>();
        foreach (KeyValuePair<string, string> pair in phrasesToUrls)
        {
            // Replace phrases with placeholders.
            string placeholder = Guid.NewGuid().ToString();
            text = text.Replace(pair.Key, placeholder);
            // Create dictionary of links by placeholder
            string link = string.Format(
                "<a href='"{0}'">{1}</a>",
                pair.Value,
                pair.Key);
            placeholdersToLinks.Add(placeholder, link);
        }
        // Phase 2: replace unique placeholders with links.
        foreach (KeyValuePair<string, string> pair in placeholdersToLinks)
        {
            text = text.Replace(pair.Key, pair.Value);
        }
        return text;
    }
    public int Compare(string x, string y)
    {
        if (x.Length > y.Length) return -1;
        if (x.Length < y.Length) return +1;
        // Equal length strings still need to be differentiated, otherwise
        // they will be treated as the same key by the  dictionary.
        return x.CompareTo(y);
    }
}

这里有一个使用它的例子:

string input = "London is a great city and have football events " +
    "in London but party sites in London are also good. London " +
    "football events are great along with London party sites. " +
    "Enjoy London!";
TextLinker linker = new TextLinker();
linker.AddLink(
    "Football events in London",
    "http://www.mysite/footbal-events/london");
linker.AddLink(
    "football events in London",
    "http://www.mysite/footbal-events/london");
linker.AddLink(
    "London football events",
    "http://www.mysite/footbal-events/london");
linker.AddLink(
    "London",
    "http://www.mysite/london-events/london");
linker.AddLink(
    "Party sites in London",
    "http://www.mysite/party-sites/london");
linker.AddLink(
    "party sites in London",
    "http://www.mysite/party-sites/london");
linker.AddLink(
    "London party sites",
    "http://www.mysite/party-sites/london");
string output = linker.Link(input);

您还可以重载AddLink方法以自动生成具有可选大小写的短语。

如果您首先用URL替换所有较长的字符串,而不是在URL中设置"London",您可以设置其他单词,例如"Lxondon"?在将包含London的所有字符串替换为相应的url之后,还可以将London替换为其URI。最后,你将把所有文本中的"Lxondon"替换为"London"。

这不是一个很好的方法,但我认为它会工作。

如果London首先被替换,那么您的其他regex字符串将不再存在于文本中。

伦敦足球赛事

现在

伦敦足球赛事

要详细说明其他答案,您必须将最长和更复杂的字符串替换放在第一位。如:

伦敦足球赛事

伦敦

如果你像你的例子一样输入London,并将其替换为Kent,那么任何"Football events in London"的实例都将变成"Football events in Kent",并且将不满足正则表达式。

PS:如果你经常使用它,你可能想要考虑将它作为string的扩展方法。

如果递归地进行替换会怎么样?也就是说,每次找到匹配项时,将其替换为字典中的文本并重复此过程,但仅针对未匹配的文本部分。

正如其他人所说:

  1. 如果您在"Football events London"之前替换"London",则您搜索的"Football events London"将不匹配"Football events London"
  2. 如果您在"London"之前替换"Football events London",您将在现有的"Football events London"链接中替换"London",这将为您提供一个链接中的链接…
  3. 字典没有排序,所以在任何一种情况下,你都不能保证你会得到你想要的顺序,如果你只是通过foreach
  4. 如果你的搜索文本也包含在你的url中,你的代码也会找到那些并替换它们——尤其是当你使你的正则表达式不区分大小写时。
  5. 在a标签的文本中包含一个前导空格?这是一个信号,表明你在其他地方做错了,你用"hack"来补偿它。

故事的寓意:查找和替换(即使使用Regex)恐怕也不能解决问题。

可能有更聪明的方法来做到这一点,但在我的脑海里,这里有一些东西需要研究,关于伪代码:

while(!input.EOS)
   for(longest to shortest key)
      if(input.indexOf(key) = 0)
          output += input.replace(key, url)
          input = remained of input
          matched = true
   if !matched then move first word from input to output

你将不得不摆弄一下它,特别是因为空白的问题(如何/在哪里匹配空格和非单词字符?)这里有另一个技巧让你开始:^'s*(.+?)'s*'b

你可以做的一件事是:

将键(从最大到最小)连接到单个正则表达式中(假设dictionaryIDictionary<string, string>):

var pattern = string.Join(
    "|",
    dictionary.Keys.OrderByDescending(k => k.Length).Select(Regex.Escape).ToArray()
);
var regex = new Regex("(" + pattern + ")", RegexOptions.ExplicitCapture);

注意在转换函数中使用Regex.Escape:我们不希望在键中使用特殊的regex字符来把事情搞砸。

一个快速的测试表明,. net的正则表达式引擎将按照它们在模式中出现的顺序尝试匹配。这意味着,当排序正确时,将首先尝试较长的键,然后regex将继续查找新的匹配项。

然后,您可以循环遍历匹配项并根据旧字符串构建新字符串,而不是多次扫描输入字符串。这两种技术结合起来将消除两个问题:过早的重复匹配。

string input = "..."; // This is your input string.
int last = 0;
var output = new StringBuilder(input.Length);
foreach (Match match in regex.Matches(input)) {
    output.Append(input.Substring(last, match.Index - last); // Appends text between matches.
    output.AppendFormat(
        "<a href='"{1}'">{0}</a>",
        match.Value,
        dictionary[match.Value]
    );
    last = match.Index + match.Length; // Moves the index to the end of this match.
}

不包括错误检查。此外,正则表达式本身也可能受益于'b(...)'b形式的'b锚。不过这是未经测试的,我要去睡觉了。