【问题标题】:How to replace various string occurrences in multiple strings the fastest way如何以最快的方式替换多个字符串中出现的各种字符串
【发布时间】:2020-02-11 12:22:49
【问题描述】:

我有一个常见问题,我没有找到合适的解决方案。我有多个带有特定标签(例如 MIME_SOURCE)的 XML 字符串,但我不知道哪个 XML 字符串包含哪个值。但我必须替换所有出现的地方。

另一方面,我有一个字典,其中包含 XML 的所有可能值作为键和要替换为值的值。正如我所说,我不知道在哪个 XML 中替换什么。

例如

第一个 XML 的一部分

    <MIME>
        <MIME_SOURCE>\Web\Bilder Groß\1509_131_021_01.jpg</MIME_SOURCE>
    </MIME>
    <MIME>
        <MIME_SOURCE>\Web\Bilder Groß\1509_131_021_01_MitWasserzeichen.jpg</MIME_SOURCE>
    </MIME>
    <MIME>
        <MIME_SOURCE>\Web\Bilder Groß\icon_top.jpg</MIME_SOURCE>
    </MIME>

第二个 XML 的一部分:

    <MIME>
        <MIME_SOURCE>\Web\Bilder klein\5478.jpg</MIME_SOURCE>
    </MIME>

字典看起来像:

{"\Web\Bilder Groß\1509_131_021_01.jpg", "/Web/Bilder Groß/1509_131_021_01.jpg"}

{"\Web\Bilder Groß\1509_131_021_01_MitWasserzeichen.jpg", "/Web/Bilder Groß/1509_131_021_01_MitWasserzeichen.jpg"}

{"\Web\Bilder Groß\icon_top.jpg", "icon_top.jpg"}

{"\Web\Bilder klein\5478.jpg", "5478.jpg"}

我的主要问题是,如果我为每个 XML 字符串遍历字典,那么工作量将是 XML 字符串的数量乘以字典中的条目数 (n*m)。这对我来说真的很糟糕,因为字典中可能有大约一百万个 XML 字符串和至少数千个条目。

目前我正在为每个 XML 字典的每个键使用 string.Replace。

你知道如何加快这个过程吗?


编辑:

我已将代码更改为以下代码:

        var regex = new Regex(@"<MIME_SOURCE>[\s\S]*?<\/MIME_SOURCE>");

        foreach (Match match in regex.Matches(stringForXml))
        {
            DoReplacements...
        }

这符合现在的要求,因为替换只会针对 XML 中的每个 MIME_SOURCE 进行。但我也会看看上面提到的算法。

【问题讨论】:

  • 前段时间我也遇到过类似的问题,看看这个问题,或许能帮到你:stackoverflow.com/questions/46339057/…
  • 好的,谢谢!这似乎是一个非常相似的问题。我会检查算法。

标签: c# xml string performance replace


【解决方案1】:

最正确的方法是正确解析你的XML。然后你可以一次通过它:

var xml = @"<root>
    <MIME>
        <MIME_SOURCE>\Web\Bilder Groß\1509_131_021_01.jpg</MIME_SOURCE>
    </MIME>
    <MIME>
        <MIME_SOURCE>\Web\Bilder Groß\1509_131_021_01_MitWasserzeichen.jpg</MIME_SOURCE>
    </MIME>
    <MIME>
        <MIME_SOURCE>\Web\Bilder Groß\icon_top.jpg</MIME_SOURCE>
    </MIME>
</root>";

var replacements = new Dictionary<string, string>()
{
    {@"\Web\Bilder Groß\1509_131_021_01.jpg", "/Web/Bilder Groß/1509_131_021_01.jpg"},
    {@"\Web\Bilder Groß\1509_131_021_01_MitWasserzeichen.jpg", "/Web/Bilder Groß/1509_131_021_01_MitWasserzeichen.jpg"},
    {@"\Web\Bilder Groß\icon_top.jpg", "icon_top.jpg"},
    {@"\Web\Bilder klein\5478.jpg", "5478.jpg"}
};

var doc = XDocument.Parse(xml);
foreach (var source in doc.Root.Descendants("MIME_SOURCE"))
{
    if (replacements.TryGetValue(source.Value, out var replacement))
    {
        source.Value = replacement;
    }
}

var result = doc.ToString();

如果您可以对 XML 的结构做出一些假设(例如,&lt;MINE_SOURCE&gt; 标记之间没有空格,没有属性等),那么您可以使用一些正则表达式,让您再次进行一次传递:

var result = Regex.Replace(xml, @"<MIME_SOURCE>([^<]+)</MIME_SOURCE>", match =>
{
    if (replacements.TryGetValue(match.Groups[1].Value, out var replacement))
    {
        return $"<MIME_SOURCE>{replacement}</MIME_SOURCE>";
    }
    return match.Value;
});

您必须自己根据自己的数据对不同的方法进行基准测试。使用BenchmarkDotNet。

【讨论】:

  • 感谢您的建议 :) XML 格式正确,因此标签中没有任何空格或类似的东西。不幸的是,在我的情况下使用 XDoc 受到限制,因为使用了太多内存......否则我想使用这种方法。
  • @Christoph 您的 XML 文件有多大?
【解决方案2】:

正如我在上面的评论中已经提到的,我曾经遇到过类似的问题(请参阅:c# Fastest string search in all files)。

使用在接受的答案中向我建议的Aho–Corasick algorithm,我能够在足够快的时间内针对我的问题进行字符串搜索(执行时间从几分钟缩短到几秒钟)。

上述算法的实现可以在here找到。

这里有一个关于如何使用上面链接的实现的小示例。 (大海捞针)

static bool anyViaAhoCorasick(string[] needles, string haystack)
{
    var trie = new Trie();
    trie.Add(needles);
    trie.Build();
    return trie.Find(haystack).Any();
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-28
    相关资源
    最近更新 更多