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

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

245263 次浏览

最有效的方法是使用StringBuilder,如下所示:

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

@jonezy:字符串。Concat是好的,如果你有一些小东西。但是,如果您正在连接兆字节的数据,那么您的程序可能会崩溃。

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

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

String.Join唯一的问题是你必须用一个通用的分隔符连接字符串。

正如@ryanversaw指出的那样,可以将分隔符设置为string.Empty

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

对于只有两个字符串,您肯定不希望使用StringBuilder。如果超过某个阈值,StringBuilder开销将小于分配多个字符串的开销。

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

Chinh Do - StringBuilder并不总是更快:

经验法则

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

  • 连接超过三个动态字符串值时,使用StringBuilder

  • 当从多个字符串字面值构建一个大字符串时,可以使用@字符串字面值或内联+操作符。

StringBuilder是你最好的选择,但在那篇文章中显示的情况下,你至少应该考虑每种情况。

这取决于代码。 StringBuilder通常更高效,但如果您只是连接几个字符串并在一行中完成所有操作,那么代码优化可能会为您解决这个问题。考虑代码的外观也很重要:对于较大的集合,StringBuilder将使其更容易阅读,对于较小的集合,StringBuilder只会增加不必要的混乱

如果你在循环中操作,StringBuilder可能是一种方法;它节省了定期创建新字符串的开销。不过,在只运行一次的代码中,String.Concat可能没问题。

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

Rico马里安尼, . net性能大师,在这个问题上有一篇文章。这并不像人们想象的那么简单。基本的建议是:

如果你的图案是这样的:

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

这是一个concat,它是活泼的,StringBuilder可能不会有帮助。

如果你的图案是这样的:

< p > if (...) x += f1(...) < br > if (...) x += f2(...) < br > if (...) x += f3(...) < br > if (...) x += f4(...) < / p >

那么你可能需要StringBuilder。

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

从这个MSDN文章:

有一些开销与 创建一个StringBuilder对象,两者都有 在时间和记忆中。在一台机器上 快速内存,StringBuilder变成 如果你要做5次,那就值得了 操作。根据经验,我 10个或更多的字符串操作 开销是否合理

.

.

因此,如果你信任MSDN,如果你必须做超过10个字符串操作/连接,那么就使用StringBuilder -否则简单的字符串连接'+'就可以了。

这真的取决于你的使用模式。 字符串之间的详细基准测试。连接,字符串,Concat和字符串。格式可以在这里找到:字符串。格式不适合密集的日志记录

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

字符串连接有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()

的字符串。Concat方法在这里等价于字符串。用空分隔符连接方法调用。添加空字符串很快,但不这样做更快,因此字符串。Concat方法在这里更优。

同样重要的是,如果要连接字符串字面值,则应该使用+操作符。

当您使用+运算符连接字符串字面值或字符串常量时,编译器将创建一个单独的字符串。没有发生运行时连接。

如何:串联多个字符串(c#编程指南)

系统。字符串是不可变的。当我们修改一个字符串变量的值时,一个新的内存被分配给新的值,之前的内存分配被释放。系统。StringBuilder被设计为具有可变字符串的概念,其中可以执行各种操作,而无需为修改后的字符串分配单独的内存位置。

除了其他答案,请记住StringBuilder可以被告知要分配的初始内存量. c。

能力参数定义了当前实例分配的内存中可以存储的最大字符数。它的值被赋给能力属性。如果当前实例中存储的字符数超过了这个能力值,StringBuilder对象会分配额外的内存来存储这些字符。

如果能力为0,则使用特定于实现的默认容量。

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

如果你知道最终字符串的长度,可以简单地计算它,或者可以对常见情况做出有根据的猜测(分配太多并不一定是坏事),你应该将此信息提供给构造函数或能力属性。特别是当运行性能测试来比较StringBuilder和其他方法,如String。Concat,它们在内部做同样的事情。你在网上看到的任何没有在比较中包含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();
}

你会发现第一个代码将很快结束,内存将在一个很好的数量。

第二个代码可能内存没问题,但它会花费更长的时间…更长的时间。 所以如果你有一个用户很多的应用程序,你需要速度,使用第一个。如果你有一款短期单用户应用,也许你可以同时使用两款应用,或者对开发者来说第二款应用更“自然”

欢呼。

下面可能是连接多个字符串的另一种解决方案。

String str1 = "sometext";
string str2 = "some other text";


string afterConcate = $"{str1}{str2}";

字符串插值

这是我为我的大规模NLP应用程序进化了十多年来最快的方法。我有IEnumerable<T>和其他输入类型的变体,有和没有不同类型的分隔符(CharString),但在这里我将连接数组中的所有字符串的简单情况显示为单个字符串,没有分隔符。这里的最新版本是在C # 7net 4.7上开发和单元测试的。

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

接下来,给定最终字符串的总分配大小,就地构建结果字符串将获得最大的性能提升。要做到这一点,需要使用(可能有争议的)技术暂时暂停新的String的不可变性,该String最初被分配为全0。然而,抛开这些争议不谈……

...请注意,这是本页上唯一完全避免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方法。

另一个解决方案:

在循环中,使用List而不是string。

List<string> lst= new List<string>();


for(int i=0; i<100000; i++){
...........
lst.Add(...);
}
return String.Join("", lst.ToArray());;

它非常非常快。

我已经测试了本页中的所有方法,最后我开发出了最快且内存成本更低的解决方案。

注:在Framework 4.8中测试

enter image description here

 [MemoryDiagnoser]
public class StringConcatSimple
{
private string
title = "Mr.", firstName = "David", middleName = "Patrick", lastName = "Callan";


[Benchmark]
public string FastConcat()
{
return FastConcat(
title, " ",
firstName, " ",
middleName, " ",
lastName);
}


[Benchmark]
public string StringBuilder()
{
var stringBuilder =
new StringBuilder();


return stringBuilder
.Append(title).Append(' ')
.Append(firstName).Append(' ')
.Append(middleName).Append(' ')
.Append(lastName).ToString();
}


[Benchmark]
public string StringBuilderExact24()
{
var stringBuilder =
new StringBuilder(24);


return stringBuilder
.Append(title).Append(' ')
.Append(firstName).Append(' ')
.Append(middleName).Append(' ')
.Append(lastName).ToString();
}


[Benchmark]
public string StringBuilderEstimate100()
{
var stringBuilder =
new StringBuilder(100);


return stringBuilder
.Append(title).Append(' ')
.Append(firstName).Append(' ')
.Append(middleName).Append(' ')
.Append(lastName).ToString();
}


[Benchmark]
public string StringPlus()
{
return title + ' ' + firstName + ' ' +
middleName + ' ' + lastName;
}


[Benchmark]
public string StringFormat()
{
return string.Format("{0} {1} {2} {3}",
title, firstName, middleName, lastName);
}


[Benchmark]
public string StringInterpolation()
{
return
$"{title} {firstName} {middleName} {lastName}";
}


[Benchmark]
public string StringJoin()
{
return string.Join(" ", title, firstName,
middleName, lastName);
}


[Benchmark]
public string StringConcat()
{
return string.
Concat(new String[]
{ title, " ", firstName, " ",
middleName, " ", lastName });
}
}

是的,它使用不安全

public static unsafe string FastConcat(string str1, string str2, string str3, string str4, string str5, string str6, string str7)
{
var capacity = 0;


var str1Length = 0;
var str2Length = 0;
var str3Length = 0;
var str4Length = 0;
var str5Length = 0;
var str6Length = 0;
var str7Length = 0;


if (str1 != null)
{
str1Length = str1.Length;
capacity = str1Length;
}


if (str2 != null)
{
str2Length = str2.Length;
capacity += str2Length;
}


if (str3 != null)
{
str3Length = str3.Length;
capacity += str3Length;
}


if (str4 != null)
{
str4Length = str4.Length;
capacity += str4Length;
}


if (str5 != null)
{
str5Length = str5.Length;
capacity += str5Length;
}


if (str6 != null)
{
str6Length = str6.Length;
capacity += str6Length;
}


if (str7 != null)
{
str7Length = str7.Length;
capacity += str7Length;
}




string result = new string(' ', capacity);


fixed (char* dest = result)
{
var x = dest;


if (str1Length > 0)
{
fixed (char* src = str1)
{
Unsafe.CopyBlock(x, src, (uint)str1Length * 2);
x += str1Length;
}
}


if (str2Length > 0)
{
fixed (char* src = str2)
{
Unsafe.CopyBlock(x, src, (uint)str2Length * 2);
x += str2Length;
}
}


if (str3Length > 0)
{
fixed (char* src = str3)
{
Unsafe.CopyBlock(x, src, (uint)str3Length * 2);
x += str3Length;
}
}


if (str4Length > 0)
{
fixed (char* src = str4)
{
Unsafe.CopyBlock(x, src, (uint)str4Length * 2);
x += str4Length;
}
}


if (str5Length > 0)
{
fixed (char* src = str5)
{
Unsafe.CopyBlock(x, src, (uint)str5Length * 2);
x += str5Length;
}
}


if (str6Length > 0)
{
fixed (char* src = str6)
{
Unsafe.CopyBlock(x, src, (uint)str6Length * 2);
x += str6Length;
}
}


if (str7Length > 0)
{
fixed (char* src = str7)
{
Unsafe.CopyBlock(x, src, (uint)str7Length * 2);
}
}
}


return result;
}

您可以编辑该方法并使其适应您的情况。例如,你可以让它像这样

FastConcat(string str1, string str2, string str3 = null, string str4 = null, string str5 = null, string str6 = null, string str7 = null)