在C#中替换多个字符串元素


86

有没有更好的办法做到这一点...

MyString.Trim().Replace("&", "and").Replace(",", "").Replace("  ", " ")
         .Replace(" ", "-").Replace("'", "").Replace("/", "").ToLower();

我已经扩展了字符串类,以将其简化为一项工作,但是有一种更快的方法吗?

public static class StringExtension
{
    public static string clean(this string s)
    {
        return s.Replace("&", "and").Replace(",", "").Replace("  ", " ")
                .Replace(" ", "-").Replace("'", "").Replace(".", "")
                .Replace("eacute;", "é").ToLower();
    }
}

只是为了好玩(并停止评论中的论点),我已提拔要点,对以下各种示例进行基准测试。

https://gist.github.com/ChrisMcKee/5937656

regex选项的得分非常高;字典选项出现最快;stringbuilder replace的长发版本比短手稍快。


1
根据基准测试中的内容,字典版本似乎并没有进行所有替换,我怀疑这是使它比StringBuilder解决方案更快的原因。
蟾蜍

1
@toad Hi,来自2009年;我在4月以下添加了关于该明显错误的评论。尽管我跳过了D,但要点已更新。字典版本仍然更快。
克里斯·麦基


1
@TotZam至少在标记事物之前检查日期;这是从2009年开始,从2012年开始
克里斯·麦基

由于这里的许多答案似乎都与性能有关,我相信应该指出,安德烈·阿达曼科的答案可能是许多替代品中最快的;肯定比chain.Replace()更快,尤其是在他的答案中所述的大型输入字符串上。
person27年

Answers:


123

更快-不 更有效-是的,如果您要使用StringBuilder该类。在您的实现中,每个操作都会生成一个字符串副本,在某些情况下可能会影响性能。字符串是不可变的对象,因此每个操作仅返回修改后的副本。

如果您希望在Strings相当长的整数倍处主动调用此方法,则最好将其实现“迁移”到StringBuilder类上。有了它,任何修改都可以直接在该实例上执行,因此可以节省不必要的复制操作。

public static class StringExtention
{
    public static string clean(this string s)
    {
        StringBuilder sb = new StringBuilder (s);

        sb.Replace("&", "and");
        sb.Replace(",", "");
        sb.Replace("  ", " ");
        sb.Replace(" ", "-");
        sb.Replace("'", "");
        sb.Replace(".", "");
        sb.Replace("eacute;", "é");

        return sb.ToString().ToLower();
    }
}

2
为了清楚起见,字典的答案是最快的stackoverflow.com/a/1321366/52912
克里斯·麦基

3
gist.github.com/ChrisMcKee/5937656上的基准测试中,字典测试不完整:它不会进行所有替换,并且会替换“”而不是“”。并非所有替换都可能是原因,为什么它是基准测试中最快的。正规表达式替换也未完成。但最重要的是,您的字符串TestData非常短。像已接受的答案状态一样,字符串必须具有足够的长度才能使StringBuilder发挥作用。您能否以10kB,100kB和1MB的字符串重复基准测试?
Leif 2014年

这是一个好点;就目前而言,它被用于URL清理,因此以100kb-1mb进行测试将是不现实的。我将更新基准,以便将其全部使用,这是一个错误。
克里斯·麦基

为了获得最佳性能,请遍历字符并自己替换它们。但是,如果您有多个字符串,那么这可能很麻烦(查找它们会强制您一次比较多个字符,而替换它们需要分配更多的内存并移动字符串的其余部分)。
Chayim Friedman

13

这样会更有效:

public static class StringExtension
{
    public static string clean(this string s)
    {
        return new StringBuilder(s)
              .Replace("&", "and")
              .Replace(",", "")
              .Replace("  ", " ")
              .Replace(" ", "-")
              .Replace("'", "")
              .Replace(".", "")
              .Replace("eacute;", "é")
              .ToString()
              .ToLower();
    }
}

真的很难读。我相信您知道它的作用,但是初级开发人员会为实际发生的事情挠头。我同意-我也一直在寻找写东西的捷径-但这只是为了我自己的满意。其他人吓得一团糟。
Piotr Kula

3
这实际上要慢一些。BenchmarkOverhead ... 13ms StringClean-user151323 ... 2843ms StringClean-TheVillageIdiot ... 2921ms重新运行时有所不同,但答案胜出gist.github.com/anonymous/5937596
Chris McKee

12

如果您只是在寻求一个漂亮的解决方案而又不需要节省几纳秒的时间,那么一些LINQ糖又如何呢?

var input = "test1test2test3";
var replacements = new Dictionary<string, string> { { "1", "*" }, { "2", "_" }, { "3", "&" } };

var output = replacements.Aggregate(input, (current, replacement) => current.Replace(replacement.Key, replacement.Value));

类似于要点中的示例C(如果在其上方看时,注释中会显示较丑的linq语句)
Chris McKee 2014年

1
有趣的是,您将功能性陈述定义为“ Uglier”而非程序性陈述。
TimS

不打算争论它;它只是偏爱。就像您说的那样,Linq只是语法糖。正如我说的那样,我已经在代码上方添加了等效项:)
克里斯·麦基

11

也许更具可读性?

    public static class StringExtension {

        private static Dictionary<string, string> _replacements = new Dictionary<string, string>();

        static StringExtension() {
            _replacements["&"] = "and";
            _replacements[","] = "";
            _replacements["  "] = " ";
            // etc...
        }

        public static string clean(this string s) {
            foreach (string to_replace in _replacements.Keys) {
                s = s.Replace(to_replace, _replacements[to_replace]);
            }
            return s;
        }
    }

还要添加New In Town关于StringBuilder的建议...


5
这样更易读:private static Dictionary<string, string> _replacements = new Dictionary<string, string>() { {"&", "and"}, {",", ""}, {" ", " "} /* etc */ };
ANeves认为SE是邪恶的

2
或当然是...私有静态只读Dictionary <string,string>替换= new Dictionary <string,string>(){{“&”,“ and”},{“,”,“”},{“”, “”} / *等* /}; 公共静态字符串Clean(此字符串s){返回Replacements.Keys.Aggregate(s,(current,toReplace)=> current.Replace(toReplace,Replacements [toReplace])); }
克里斯·麦基

2
-1:在这里使用字典没有任何意义。只需使用即可List<Tuple<string,string>>。这也改变了替换的顺序,并且速度不如例如s.Replace("a").Replace("b").Replace("c")。不要使用这个!
托马斯

6

在建议的解决方案中可能有一件事可以优化。进行多次调用Replace()会使代码对同一字符串进行多次传递。对于非常长的字符串,由于CPU缓存容量丢失,解决方案可能会很慢。也许应该考虑一次更换多个字符串


1
许多答案似乎都与性能有关,在这种情况下,这是最好的。这很简单,因为它只是String.Replace的记录重载,您可以在其中基于匹配项返回期望值,在此示例中,使用字典将它们匹配。应该简单易懂。
person27

4

使用linq的另一种选择是

[TestMethod]
public void Test()
{
  var input = "it's worth a lot of money, if you can find a buyer.";
  var expected = "its worth a lot of money if you can find a buyer";
  var removeList = new string[] { ".", ",", "'" };
  var result = input;

  removeList.ToList().ForEach(o => result = result.Replace(o, string.Empty));

  Assert.AreEqual(expected, result);
}

您可以声明var removeList = new List<string> { /*...*/ };然后只调用removeList.ForEach( /*...*/ );并简化您的代码。另请注意,由于找到的所有字符串都被替换为,因此无法完全回答问题String.Empty
Tok'17年

2

我正在做类似的事情,但就我而言,我正在进行序列化/反序列化,因此我需要能够双向执行。我发现使用string [] []与字典几乎相同,包括初始化,但是您也可以朝另一个方向进行操作,将替代项返回其原始值,这实际上是字典不准备执行的操作。

编辑:您可以使用Dictionary<Key,List<Values>>以获得与string [] []相同的结果


-1
string input = "it's worth a lot of money, if you can find a buyer.";
for (dynamic i = 0, repl = new string[,] { { "'", "''" }, { "money", "$" }, { "find", "locate" } }; i < repl.Length / 2; i++) {
    input = input.Replace(repl[i, 0], repl[i, 1]);
}

2
您应该考虑在答案中添加上下文。像是对它在做什么的简要说明,以及相关的原因,为什么要以这种方式编写。
尼尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.