最有效的方法来连接字符串?

连接字符串的最有效的方法是什么?

StringBuilder.Append()方法比使用+运算符好得多。 但是我发现,当执行1000个或更少的连接时, String.Join()StringBuilder更有效率。

 StringBuilder sb = new StringBuilder(); sb.Append(someString); 

String.Join唯一的问题是你必须用一个公共的分隔符连接字符串。 (编辑:)如@ryanversaw指出,你可以使分隔字符串.Empty。

 string key = String.Join("_", new String[] { "Customers_Contacts", customerID, database, SessionID }); 

.NET Performance guru Rico Mariani就这个问题写了一篇文章 。 这并不像人们怀疑的那么简单。 基本的建议是这样的:

如果你的模式看起来像:

x = f1(...) + f2(...) + f3(...) + f4(...)

这是一个concat,它是zippy,StringBuilder可能不会帮助。

如果你的模式看起来像:

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

那么你可能需要StringBuilder。

另一篇支持这一说法的文章来自Eric Lippert,他在那里详细描述了在一行+连接上进行的优化。

有6种类型的字符串连接:

  1. 使用加号( + )符号。
  2. 使用string.Concat()
  3. 使用string.Join()
  4. 使用string.Format()
  5. 使用string.Append()
  6. 使用StringBuilder

在一个实验中,已经证明了如果单词少于1000(大约), string.Concat()是最好的方法,如果单词大于1000,那么应该使用StringBuilder

欲了解更多信息,请查看本网站 。

string.Join()vs string.Concat()

这里的string.Concat方法等价于使用空分隔符的string.Join方法调用。 附加一个空字符串是快速的,但不这样做更快,所以string.Concat方法在这里会更好。

来自Chinh – StringBuilder并不总是更快 :

经验法则

  • 连接三个动态字符串值或更少时,使用传统字符串连接。

  • 当连接三个以上的动态字符串值时,使用StringBuilder。

  • 从几个字符串文字构建一个大字符串时,请使用@字符串文字或内联运算符。

大部分时间StringBuilder是你最好的选择,但是在这个帖子中有一些情况应该至少考虑一下每个情况。

如果你在循环操作,StringBuilder可能是要走的路; 它可以节省您定期创建新字符串的开销。 在只能运行一次的代码中,String.Concat可能没问题。

然而,Rico Mariani(.NET优化大师) 做了一个测验 ,他在最后说道,在大多数情况下,他推荐使用String.Format。

从这个MSDN文章 :

在创建一个StringBuilder对象的时候和内存都有一些开销。 在具有快速内存的机器上,如果您正在执行五个操作,那么一个StringBuilder就会变得有价值。 作为一个经验法则,我会说10个或更多的字符串操作是在任何机器上开销的理由,即使是较慢的。

所以,如果你信任MSDN去与StringBuilder,如果你必须做超过10个字符串操作/连接 – 否则简单的字符串连字符与'+'是好的。

添加到其他答案,请记住, 可以告诉StringBuilder分配的初始内存量 。

容量参数定义当前实例分配的内存中可以存储的最大字符数。 它的值被分配给Capacity属性。 如果当前实例中要存储的字符数超过此容量值,则StringBuilder对象会分配额外的内存来存储它们。

如果容量为零,则使用实现特定的默认容量。

反复附加到未预先分配的StringBuilder可能会导致大量不必要的分配,就像重复连接常规字符串一样。

如果你知道最后一个字符串会有多长,可以简单地计算出来,或者可以对普通情况进行有根据的猜测(分配太多不一定是坏事),你应该把这个信息提供给构造函数或者容量属性。 特别是在运行性能测试来比较StringBuilder与其他方法如String.Concat,它们在内部执行相同的操作。 你在网上看到的任何不包含StringBuilder预分配的测试都是错误的。

如果你不能猜测这个大小,你可能正在编写一个实用程序函数,它应该有自己的可选参数来控制预分配。

指出这一点也很重要,如果串联字符串文字 ,应该使用+运算符。

当通过使用+运算符连接字符串文字或字符串常量时,编译器会创建一个字符串。 没有运行时串联发生。

如何连接多个字符串(C#编程指南)

最有效的是使用StringBuilder,就像这样:

 StringBuilder sb = new StringBuilder(); sb.Append("string1"); sb.Append("string2"); ...etc... String strResult = sb.ToString(); 

@jonezy:String.Concat是好的,如果你有一些小事情。 但是,如果你连接了兆字节的数据,你的程序可能会消失。

这真的取决于你的使用模式。 string.Join,string,Concat和string.Format之间的详细基准可以在这里找到: String.Format不适用于强化日志记录

(这实际上是我给这个问题的同样的答案)

System.String是不可变的。 当我们修改一个字符串变量的值时,一个新的内存被分配给新的值,并释放先前的内存分配。 System.StringBuilder被设计为具有可变字符串的概念,其中可以执行各种操作,而不用为修改的字符串分配单独的存储器位置。

试试这两个代码,你会发现解决方案。

  static void Main(string[] args) { StringBuilder s = new StringBuilder(); for (int i = 0; i < 10000000; i++) { s.Append( i.ToString()); } Console.Write("End"); Console.Read(); } 

VS

 static void Main(string[] args) { string s = ""; for (int i = 0; i < 10000000; i++) { s += i.ToString(); } Console.Write("End"); Console.Read(); } 

你会发现第一个代码将会非常快速地结束,并且内存将会很大。

第二个代码也许内存会好的,但是会花费更长的时间。 所以,如果你有很多用户的应用程序,并且需要速度,那就使用1st。 如果你有一个短期的一个用户应用程序的应用程序,也许你可以使用两个或第二个将更加“自然”的开发人员。

干杯。

以下可能是多个替代解决方案来连接多个字符串。

 String str1 = "sometext"; string str2 = "some other text"; string afterConcate = $"{str1}{str2}"; 

字符串插值

对于只有两个字符串,你绝对不想使用StringBuilder。 有一些阈值高于StringBuilder的开销小于分配多个字符串的开销。

所以,对于更多的2-3个字符串,使用DannySmurf的代码 。 否则,只需使用+运算符。

这将取决于代码。 StringBuilder一般来说效率更高,但是如果你只连接了几个字符串并在一行中完成,那么代码优化可能会为你处理。 思考代码的外观也很重要:对于较大的设置,StringBuilder会使读起来更容易,对于小的StringBuilder只会增加不必要的混乱。

这是我十多年来为我的大型NLP应用程序演变而来的最快速的方法。 我有IEnumerable<T>和其他输入类型的变体,有和没有不同类型( CharString )的分隔符,但在这里我展示了一个简单的例子, 将数组中的所有字符串连接成一个单独的字符串,没有分隔符。 这里的最新版本是在C#7.NET 4.7上开发和单元测试的。

有两个关键要更高的性能; 首先是预先计算所需的确切总大小。 当输入是一个数组时,这一步是微不足道的,如下所示。 为了处理IEnumerable<T>相反,它是值得首先收集的字符串到一个临时数组计算total(该数组需要避免每个元素多次调用ToString()从技术上说,考虑到副作用的可能性,这样做可能会改变“字符串连接”操作的预期语义)。

接下来,给定最终字符串的总分配大小,通过就地生成结果字符串来获得最大的性能提升。 这样做需要暂时中止一个新的String的不可变性的(也许是有争议的)技术,该String最初被分配了满零。 抛开任何这样的争议,但是…

…请注意,这是这个页面上唯一的批量连接解决方​​案,完全避免了由String构造函数进行额外的一轮分配和复制

完整的代码:

 /// <summary> /// Concatenate the strings in 'rg', none of which may be null, into a single String. /// </summary> public static unsafe String StringJoin(this String[] rg) { int i; if (rg == null || (i = rg.Length) == 0) return String.Empty; if (i == 1) return rg[0]; String s, t; int cch = 0; do cch += rg[--i].Length; while (i > 0); if (cch == 0) return String.Empty; i = rg.Length; fixed (Char* _p = (s = new String(default(Char), cch))) { Char* pDst = _p + cch; do if ((t = rg[--i]).Length > 0) fixed (Char* pSrc = t) memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1)); while (pDst > _p); } return s; } [DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)] static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb); 

我应该提到,这个代码与我自己使用的有些微小的修改。 在原来的,我称 C#cpblk IL指令来做实际的复制。 为了简化和代码的可移植性,我将其替换为P / Invoke memcpy ,如您所见。 为了在x64上获得最高性能( 但也许不是x86 ),您可能需要使用cpblk方法。