【问题标题】:Best algorithm for inserting text before and after a token in C#?在 C# 中的标记前后插入文本的最佳算法?
【发布时间】:2010-10-08 20:43:48
【问题描述】:

我正在为 MVC C# 应用程序开发一个搜索功能,该应用程序将通过过滤器放置一个(可能很大的)文本块,并给出搜索查询,将放置一个带有突出显示样式的 html <span> 和在每个搜索词之后。

我有一个简单的算法,但我感觉它会很慢,可能是因为需要创建的字符串数量(2 * 匹配数)。

public static string Surround(string original, string head, string tail, string match, StringComparison comparer)
{
    var ret = original;

    if (ret.IndexOf(match, 0, comparer) != -1)
    {
        var lastIndex = 0;

        while ((lastIndex = ret.IndexOf(match, lastIndex, comparer)) != -1)
        {
            ret = ret.Insert(lastIndex, head);
            var tailIndex = lastIndex + match.Length + head.Length;
            lastIndex = tailIndex;
            ret = ret.Insert(tailIndex, tail);
        }
    }

    return ret;
}

我想知道是否有人可以为更好的算法提供一些提示,以更好地处理大块文本?我正在考虑使用字符串生成器,但我也想到我可能会以完全错误的方式处理这个问题。任何见解将不胜感激。

【问题讨论】:

  • 是 jQuery 的一个选项,还是必须在服务器端完成?
  • jQuery 确实可以作为一种选择,但我不知道我将如何处理它。由于这是一个数据格式问题,最好在视图(客户端)中进行。我也很想知道其他人对此的看法。

标签: c# search highlight


【解决方案1】:

StringBuilder 当然会快得多,但是当您执行 .Insert 时,您每次都会移动大量数据。因此,最好仅使用 .Append 在 StringBuilder 中构建结果。不是。插入。

但是,您也应该能够为此使用正则表达式,尽管我不完全了解语法(当我需要时,我会使用一个名为 RegEx Buddy 的出色工具来构建我的正则表达式。)。

编辑:这个世界上很少有人能够区分正则表达式和传输线噪声。 :-)

【讨论】:

  • 我在考虑一个正则表达式,但在实现它之前我需要做一些基准测试。对于大量文本,我认为它可能比使用所有插入内容还要慢。
  • 同意。您必须对其进行基准测试。请告诉我们你发现了什么! :-)
  • 到目前为止,我对我的基准测试结果感到非常震惊。我使用字符串插入方法显示匹配/插入 4000 个匹配项需要 324 毫秒,而使用正则表达式匹配相同数量的匹配项仅需要 4 毫秒。让我检查以确保一切都再次正常测量,但看起来很有希望。
  • 为了完整起见,我很高兴知道带有 StringBuilder 的仅附加版本需要什么!
【解决方案2】:

正则表达式可以完成这项工作,而且代码应该简单得多。但是,您需要进行测试以确定它是否确实提供了更好的性能。像这样的:

public static string Surround(
    string original, string head, string tail, string match)
{
    return Regex.Replace(
        original, match, head + "$0" + tail, RegexOptions.IgnoreCase);
}

如果你可以在保存 2N 个字符串连接时传递整个替换器,那就更好了:

public static string Surround(string original, string replacer, string match)
{
    return Regex.Replace(original, match, replacer, RegexOptions.IgnoreCase);
}

Surround("foo bar baz", "<span>$&</span>", "bar");  //call like so

【讨论】:

  • 我不确定正则表达式是否真的需要如此复杂,但是这是正确的方法
  • @annakata,是的,在我看到你的评论之前就注意到并修复了它:)
  • 我不使用 Replace 的唯一原因是它需要不区分大小写。 IE,如果我想放置一个“|”匹配前后的字符,需要显示“|test|”,以及“|TeSt|”对于查询“测试”。
  • +1...因为这是我对如何解决问题的第一个想法:D
  • 别担心,我希望你不介意我添加了下一个逻辑迭代,当然此时你基本上只有一个替换的包装器
【解决方案3】:

Luke 的回答很好,但您可能想先转义“匹配”中的任何特殊字符以防有人使用 .\^$?等在他们的搜索中(除非你当然想允许)。

ETA:允许特殊字符可以实现更强大的搜索,但也会导致意外输出,因为找到的文本将被替换为模式而不是实际匹配的文本。

【讨论】:

  • @Eric,我原本打算把它放在例子中,但我认为它会弄乱它。不过我应该在 cmets 中提到它。
【解决方案4】:

RegEx 解决方案绝对比我制作的更优雅。但是,使用 StringBuilder 通常会快一些,并且不需要对搜索词和前/后修复进行正则表达式转义。

private static string Surround(string original, string head, string tail, string match, StringComparison comparisonType)
{
  if (string.IsNullOrEmpty(original) || string.IsNullOrEmpty(match) || (string.IsNullOrEmpty(head) && string.IsNullOrEmpty(tail)))
    return original;

  var resultBuilder = new StringBuilder();
  int matchLength = match.Length;
  int lastIdx = 0;

  for (;;)
  {
    int curIdx = original.IndexOf(match, lastIdx, comparisonType);

    if (curIdx > -1)
      resultBuilder
        .Append(original, lastIdx, curIdx - lastIdx)
        .Append(head)
        .Append(original, curIdx, matchLength)
        .Append(tail);
    else
      return resultBuilder.Append(original.Substring(lastIdx)).ToString();

    lastIdx = curIdx + matchLength;
  }
}

希望你能用。

更新 做一些性能测试,我的解决方案似乎只有在您搜索“短”词时才会更快。如果单词很长(即长度> 7)正则表达式获胜,但如果单词很短(即长度

【讨论】:

  • 一种可能会有所帮助的优化是使用更大的初始容量(例如 new StringBuilder(2 * original.Length))初始化 resultBuilder。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-07
  • 2011-06-03
  • 1970-01-01
  • 1970-01-01
  • 2010-09-06
相关资源
最近更新 更多