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

时间:2011-05-04 06:35:49

标签: c# .net regex c#-3.0 replace

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

像:

  • 伦敦将被<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>

  • 伦敦的派对网站<a href="http://www.mysite/party-sites/london"> party sites in London</a>

  • 伦敦派对网站<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();
}

当我运行我的代码时,只有'伦敦'替换为'<a href="http://www.mysite/london-events/london">London</a>'而其他所有代码保持不变,但如果我从关键字中删除'伦敦'它就可以正常工作。

你可以帮我解决一下我如何匹配整个字符串。

要替换的内容和网址是假的:

由于

7 个答案:

答案 0 :(得分:2)

因为您要链接的某些短语包含您想要链接的其他短语,并且链接本身也将包含这些短语,如果您想避免棘手的正则表达式,则必须分两个阶段进行:

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

  • 您需要更换更长时间 短期前的短语 确定你不只替换部分的 短语(例如“伦敦”中的“伦敦” 足球赛事“)。
  • 您可以存储要在SortedDictionary中链接的短语和URL,并提供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方法,以自动生成具有替代大小写的短语。

答案 1 :(得分:1)

如果您首先用URL替换所有较长的字符串,而不是在URL中设置“London”,您可以设置另一个单词,例如“Lxondon”,该怎么办?将包含London的所有字符串替换为相应的URL后,您也可以用其URI替换London。最后,您将在所有文本中将“Lxondon”替换为“伦敦”。

这不是一个非常好的方法,但我认为它会起作用。

答案 2 :(得分:0)

如果伦敦首先被替换,那么你的其他正则表达字符串将不再存在于文本中。

伦敦的足球赛事

现在是

London

中的足球赛事

答案 3 :(得分:0)

要详细说明其他答案,您必须先放置最长且更复杂的字符串替换。例如

伦敦的足球赛事

伦敦

如果你在你的例子中做伦敦,并用肯特替换它,任何“伦敦足球赛事”的实例将成为“肯特的足球赛事”,并且不会满足正则表达式。

PS:如果您经常使用它,可能需要考虑将其作为字符串的扩展方法。

答案 4 :(得分:0)

如果您以递归方式进行替换,该怎么办?即,每次找到匹配项时,您都将其替换为字典中的文本并重复该过程,但仅针对尚未匹配的文本部分。

答案 5 :(得分:0)

正如其他人所说:

  1. 如果您在“伦敦足球赛事”之前替换“伦敦”,则您对“足球赛事伦敦”的搜索将不匹配“足球赛事&lt; a href =”http://etc..>伦敦&lt; a&gt; “
  2. 如果您在“伦敦”之前更换“伦敦足球赛事”,您将在伦敦足球赛事的现有链接中取代伦敦,这将为您提供链接中的链接......
  3. 字典未订购,因此在任何一种情况下,如果只是foreach,您无法保证获得所需的订单。
  4. 如果您的搜索文本也包含在您的网址中,您的代码也会找到那些并替换它们 - 特别是这种情况,因为您的正则表达式不区分大小写。
  5. 在标签文字中包含一个主要空格?这表明你在其他地方做错了什么,而你用'黑客'来补偿它。
  6. 故事的道德:我担心,发现和替换(即使使用正则表达式)也不会削减它。

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

    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

答案 6 :(得分:0)

您可以做的一件事是:

将键(从大到小)连接成一个正则表达式(假设此处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:我们不希望密钥中的特殊正则表达式字符出现错误。

快速测试表明,.NET的正则表达式引擎将按照它们在模式中出现的顺序尝试匹配。这意味着,如果订购正确,将首先尝试更长的密钥,然后正则表达式将继续,寻找新的匹配。

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

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锚点受益。这是未经测试的,我上床睡觉了。