【问题标题】:Best way to search for many guids at once in a string?在字符串中一次搜索多个 guid 的最佳方法?
【发布时间】:2012-11-07 19:53:32
【问题描述】:

我遇到了在字符串中搜索 90,000 个 GUID 的问题。我需要获取每个 GUID 的所有实例。从技术上讲,我也需要更换它们,但这是另一回事。

目前,我正在使用正则表达式单独搜索每一个。但我突然想到,通过一起搜索它们可以获得更好的性能。我过去读过有关尝试的内容,但从未使用过,但我突然想到,我可以构建一个包含所有 90,000 个 GUID 的 trie 并使用它进行搜索。

或者,也许 .NET 中有一个现有的库可以做到这一点。我突然想到,我没有理由只使用一个巨大的正则表达式就不能获得良好的性能,但这似乎行不通。

除此之外,也许我可以使用一些与 GUID 结构相关的聪明技巧来获得更好的结果。

这对我来说并不是一个真正的关键问题,但我认为我可能能够学到一些东西。

【问题讨论】:

  • 您能否提供输入和预期输出的示例?
  • 输入:一个字符串和一个 guid 列表 输出:字符串中可以找到每个 guid 的索引列表。
  • 我不明白为什么正则表达式应该是“巨大的”。我会假设一场比赛的正则表达式看起来与一百万场比赛相同?!?
  • 哦,当然,我通常可以为 guid 做一个正则表达式,然后选择我感兴趣的匹配项。这实际上可能是最好的方法。
  • 那么您在寻找 90,000 个特定 guid?

标签: c# search guid trie


【解决方案1】:

好的,这看起来不错。所以这里要清楚的是原始代码,它在示例字符串上运行了 65 秒:

   var unusedGuids = new HashSet<Guid>(oldToNewGuid.Keys);

    foreach (var guid in oldToNewGuid) {
        var regex = guid.Key.ToString();

        if (!Regex.IsMatch(xml, regex))
             unusedGuids.Add(guid.Key);
        else
            xml = Regex.Replace(xml, regex, guid.Value.ToString());
    }

新代码如下,耗时6.7s:

var unusedGuids = new HashSet<Guid>(oldToNewGuid.Keys);

var guidHashes = new MultiValueDictionary<int, Guid>();

foreach (var guid in oldToNewGuid.Keys) {
    guidHashes.Add(guid.ToString().GetHashCode(), guid);
}

var indices = new List<Tuple<int, Guid>>();

const int guidLength = 36;

for (int i = 0; i < xml.Length - guidLength; i++) {
    var substring = xml.Substring(i, guidLength);

    foreach (var value in guidHashes.GetValues(substring.GetHashCode())) {
         if (value.ToString() == substring) {
        unusedGuids.Remove(value);
        indices.Add(new Tuple<int, Guid>(i, value));
        break;
         }
    }
}

var builder = new StringBuilder();

int start = 0;
for (int i = 0; i < indices.Count; i++) {
    var tuple = indices[i];
    var substring = xml.Substring(start, tuple.Item1 - start);
    builder.Append(substring);
    builder.Append(oldToNewGuid[tuple.Item2].ToString());
    start = tuple.Item1 + guidLength;
}

builder.Append(xml.Substring(start, xml.Length - start));

xml = builder.ToString();

【讨论】:

    【解决方案2】:

    不久前我开发了一种替换大量字符串的方法,这可能很有用:

    A better way to replace many strings - obfuscation in C#

    另一种选择是使用正则表达式查找字符串中的所有 GUID,然后遍历它们并检查每个 GUID 是否是您的 GUID 集的一部分。

    基本示例,使用 Dictionary 快速查找 GUID:

    Dictionary<string, string> guids = new Dictionary<string, string>();
    guids.Add("3f74a071-54fc-10de-0476-a6b991f0be76", "(replacement)");
    
    string text = "asdf 3f74a071-54fc-10de-0476-a6b991f0be76 lkaq2hlqwer";
    
    text = Regex.Replace(text, @"[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}", m => {
      string replacement;
      if (guids.TryGetValue(m.Value, out replacement)) {
        return replacement;
      } else {
        return m.Value;
      }
    });
    
    Console.WriteLine(text);
    

    输出:

    asdf (replacement) lkaq2hlqwer
    

    【讨论】:

      【解决方案3】:

      看看 Rabin-Karp 字符串搜索算法。它非常适合在字符串中进行多模式搜索:

      http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_string_search_algorithm#Rabin.E2.80.93Karp_and_multiple_pattern_search

      【讨论】:

      • 谢谢,我一直在为这个问题想一个名字。这可能就是我要找的。​​span>
      【解决方案4】:

      使用 RegEx 不会获得良好的性能,因为它的性能天生就很差。此外,如果所有 GUID 共享相同的格式,您应该只需要一个 RegEx。 regex.Replace(input, replacement); 会这样做。

      如果您的内存中已经有 guid 列表,那么通过循环遍历该列表并像这样调用 String.Replace 会更好

       foreach(string guid in guids)
           inputString.replace(guid, replacement);
      

      【讨论】:

      • @Basic 哈哈,就像我提交我的混蛋评论一样,您将伪代码更改为 C#:p
      • @Basic 我不明白,我的意思是我的讽刺评论是个混蛋。
      猜你喜欢
      • 2021-10-29
      • 1970-01-01
      • 2016-11-22
      • 1970-01-01
      • 2012-03-30
      • 2013-12-24
      • 2014-03-23
      • 2019-01-03
      • 2023-04-10
      相关资源
      最近更新 更多