连接字符串最有效的方法是什么?
最有效的方法是使用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的代码。否则,只需使用+运算符。
StringBuilder并不总是更快:
经验法则
当连接三个或更少的动态字符串值时,使用传统的字符串连接。 当连接三个以上的动态字符串值时,请使用StringBuilder。 当从多个字符串字面值构建一个大字符串时,可以使用@字符串字面值或内联+操作符。
大多数情况下,StringBuilder是您最好的选择,但在某些情况下,如那篇文章所示,您至少应该考虑每种情况。
这取决于代码。 StringBuilder通常更高效,但如果您只是连接几个字符串并在一行中完成所有操作,那么代码优化可能会为您解决这个问题。考虑代码的外观也很重要:对于较大的集合,StringBuilder将使其更容易阅读,对于较小的集合,StringBuilder只会增加不必要的混乱。
如果你在循环中操作,StringBuilder可能是最好的选择;它节省了定期创建新字符串的开销。在只运行一次的代码中,字符串。Concat可能没问题。
然而,Rico Mariani(。NET优化大师)做了一个测试,他在测试的最后说,在大多数情况下,他建议使用String.Format。
. net性能专家Rico Mariani有一篇关于这个主题的文章。这并不像人们想象的那么简单。基本的建议是:
如果你的图案是这样的: X = f1(…)+ f2(…)+ f3(…)+ f4(…) 这是一个concat,它是活泼的,StringBuilder可能不会有帮助。 如果你的图案是这样的: 如果(…)x += f1(…) 如果(…)x += f2(…) 如果(…)x += f3(…) 如果(…)x += f4(…) 那么你可能需要StringBuilder。
另一篇支持这一说法的文章来自Eric Lippert,他详细描述了在一行+连接上执行的优化。
摘自这篇MSDN文章:
有一些开销与 创建一个StringBuilder对象,两者都有 在时间和记忆中。在一台机器上 快速内存,StringBuilder变成 如果你要做5次,那就值得了 操作。根据经验,我 10个或更多的字符串操作 开销是否合理 任何机器,即使是较慢的机器。
因此,如果你信任MSDN,如果你必须做超过10个字符串操作/连接,那么就使用StringBuilder -否则简单的字符串连接'+'就可以了。
这取决于你的使用模式。 字符串之间的详细基准测试。连接,字符串,Concat和字符串。格式可以在这里找到:字符串。格式不适合密集的日志记录
(这实际上是我对这个问题的回答)
字符串连接有6种类型:
使用加号。 使用string.Concat()。 使用string.Join()。 使用string.Format()。 使用string.Append()。 使用StringBuilder。
在一个实验中,已经证明string.Concat()是最好的方法,如果单词少于1000(大约),如果单词超过1000,那么应该使用StringBuilder。
欲了解更多信息,请访问该网站。
string.Join() vs string.Concat() 的字符串。Concat方法在这里等价于字符串。用空分隔符连接方法调用。追加一个空字符串很快,但不这样做更快,所以字符串。Concat法在这里更优越。
同样重要的是,如果要连接字符串字面量,则应该使用+运算符。
当您使用+运算符连接字符串字面值或字符串常量时,编译器将创建一个单独的字符串。没有发生运行时连接。
如何:连接多个字符串(c#编程指南)
系统。字符串是不可变的。当我们修改一个字符串变量的值时,一个新的内存被分配给新的值,之前的内存分配被释放。系统。StringBuilder被设计为具有可变字符串的概念,其中可以执行各种操作,而无需为修改后的字符串分配单独的内存位置。
除了其他答案之外,请记住StringBuilder可以被告知要分配的初始内存量。
容量参数定义了当前实例分配的内存中可以存储的最大字符数。它的值被赋给Capacity属性。如果当前实例中存储的字符数量超过了这个容量值,StringBuilder对象就会分配额外的内存来存储这些字符。 如果容量为零,则使用特定于实现的默认容量。
重复添加到未预先分配的StringBuilder可能会导致大量不必要的分配,就像重复连接常规字符串一样。
If you know how long the final string will be, can trivially calculate it, or can make an educated guess about the common case (allocating too much isn't necessarily a bad thing), you should be providing this information to the constructor or the Capacity property. Especially when running performance tests to compare StringBuilder with other methods like String.Concat, which do the same thing internally. Any test you see online which doesn't include StringBuilder pre-allocation in its comparisons is wrong.
如果你无法猜测它的大小,你可能在写一个效用函数它应该有自己的可选参数来控制预分配。
尝试这两段代码,您将找到解决方案。
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>和其他输入类型的变化,有和没有不同类型的分隔符(Char, String),但在这里我展示了将数组中的所有字符串连接到单个字符串的简单情况,没有分隔符。这里的最新版本是在c# 7和。net 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中测试
[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)