提问人:jimmij 提问时间:8/22/2008 最后编辑:Theodor Zouliasjimmij 更新时间:11/20/2022 访问量:265724
连接字符串的最有效方法?
Most efficient way to concatenate strings?
答:
最有效的方法是使用 StringBuilder,如下所示:
StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();
@jonezy:如果你有几个小东西,String.Concat 很好。但是,如果你连接了兆字节的数据,你的程序可能会崩溃。
该方法比使用运算符要好得多。但我发现,当执行 1000 个或更少的串联时,它甚至比 .StringBuilder.Append()
+
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
具有巨大的可比启动成本,只有在与非常大的字符串或非常多的串联一起使用时才有效。找出任何给定的情况都不是微不足道的。如果性能有问题,分析是你的朋友(检查 ANTS)。
string.Concat
对于两个字符串,您肯定不想使用 StringBuilder。存在某个阈值,超过该阈值,StringBuilder 开销小于分配多个字符串的开销。
因此,对于超过 2-3 个字符串,请使用 DannySmurf 的代码。否则,只需使用 + 运算符。
从 Chinh Do - StringBuilder 并不总是更快:
经验法则
连接三个或更少的动态字符串值时,请使用传统的字符串连接。
连接三个以上的动态字符串值时,请使用 .
StringBuilder
从多个字符串文本生成大字符串时,请使用字符串文本或内联 + 运算符。
@
大多数时候是你最好的选择,但有些情况如那篇文章所示,你至少应该考虑每种情况。StringBuilder
评论
这取决于代码。 StringBuilder 通常更有效率,但如果您只连接几个字符串并在一行中完成所有操作,则代码优化可能会为您处理它。考虑代码的外观也很重要:对于较大的集合,StringBuilder 将使其更易于阅读,对于较小的集合,StringBuilder 只会添加不必要的混乱。
如果你在一个循环中操作,可能是要走的路;它为您节省了定期创建新字符串的开销。但是,在代码中,只运行一次可能没问题。StringBuilder
String.Concat
然而,Rico Mariani(.NET 优化大师)编造了一个测验,他在最后表示,在大多数情况下,他建议 .String.Format
评论
.NET 性能专家 Rico Mariani 有一篇关于这个主题的文章。这并不像人们想象的那么简单。基本建议是这样的:
如果您的模式如下所示:
x = f1(...) + f2(...) + f3(...) + f4(...)
这是一个concat,它很活泼,StringBuilder 可能无济于事。
如果您的模式如下所示:
if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)
那么你可能想要 StringBuilder。
另一篇支持这一说法的文章来自 Eric Lippert,他详细描述了对一行连接执行的优化。+
评论
从这篇 MSDN 文章中:
有一些开销与 创建一个 StringBuilder 对象,两者 在时间和记忆中。在具有 快速内存,StringBuilder 成为 如果你正在做大约五个,这是值得的 操作。根据经验,我 会说 10 个或更多字符串操作 是开销的理由 任何机器,即使是较慢的机器。
因此,如果您信任 MSDN,如果您必须执行 10 个以上的字符串操作/连接,请使用 StringBuilder - 否则使用带有“+”的简单字符串连接就可以了。
这实际上取决于您的使用模式。 字符串之间的详细基准。Join、string、Concat 和 string。格式可以在这里找到: String.Format 不适合密集日志记录
(这其实和我给这个问题的答案是一样的)
有 6 种类型的字符串连接:
- 使用加号 () 符号。
+
- 用。
string.Concat()
- 用。
string.Join()
- 用。
string.Format()
- 用。
string.Append()
- 用。
StringBuilder
在实验中,已经证明,如果单词小于 1000(大约),并且如果单词超过 1000,则应使用,这是最好的方法。string.Concat()
StringBuilder
有关更多信息,请查看此网站。
字符串。Join() 与字符串。Concat()
字符串。这里的 Concat 方法等效于字符串。使用空分隔符联接方法调用。追加空字符串的速度很快,但不这样做会更快,所以字符串。Concat 方法在这里会更胜一筹。
评论
+
string.Concat()
string.Concat()
+
string.Append()
同样重要的是要指出,如果要连接字符串文字,则应使用运算符。+
使用 + 运算符连接字符串文本或字符串常量时,编译器将创建单个字符串。不发生运行时串联。
System.String 是不可变的。当我们修改字符串变量的值时,会为新值分配一个新内存,并释放先前的内存分配。System.StringBuilder 被设计为具有可变字符串的概念,在该字符串中可以执行各种操作,而无需为修改后的字符串分配单独的内存位置。
评论
除了其他答案之外,请记住,可以告诉 StringBuilder 要分配的初始内存量。
capacity 参数定义当前实例分配的内存中可以存储的最大字符数。其值分配给 Capacity 属性。如果要在当前实例中存储的字符数超过此容量值,则 StringBuilder 对象将分配额外的内存来存储这些字符。
如果容量为零,则使用特定于实现的默认容量。
重复追加到尚未预先分配的 StringBuilder 可能会导致大量不必要的分配,就像重复连接常规字符串一样。
如果您知道最终字符串的长度,可以简单地计算它,或者可以对常见情况进行有根据的猜测(分配太多不一定是坏事),则应将此信息提供给构造函数或 Capacity 属性。特别是在运行性能测试以将 StringBuilder 与 String.Concat 等其他方法进行比较时,这些方法在内部执行相同的操作。您在网上看到的任何测试,如果其比较中不包括 StringBuilder 预分配,都是错误的。
如果你不能对大小做出任何猜测,你可能正在编写一个实用函数,它应该有自己的可选参数来控制预分配。
试试这 2 段代码,你会找到解决方案。
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();
}
与
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}";
评论
String.Format
+
String.Concat
StringBuilder
这是我十多年来为我的大型 NLP 应用程序发展起来的最快方法。我有 和其他输入类型的变体,有和没有不同类型的分隔符 (, ),但在这里我展示了将数组中的所有字符串连接成一个字符串的简单情况,没有分隔符。此处的最新版本是在 C# 7 和 .NET 4.7 上开发和单元测试的。IEnumerable<T>
Char
String
提高性能有两个关键;首先是预先计算所需的确切总大小。当输入是数组时,此步骤是微不足道的,如下所示。相反,为了进行处理,首先将字符串收集到一个临时数组中以计算该总数(该数组需要避免对每个元素调用多次,因为从技术上讲,考虑到副作用的可能性,这样做可能会改变“字符串连接”操作的预期语义)。IEnumerable<T>
ToString()
接下来,给定最终字符串的总分配大小,通过就地构建结果字符串可以最大程度地提高性能。这样做需要一种(也许是有争议的)技术,即暂时暂停最初分配为零的新事物的不可变性。然而,撇开任何此类争议不谈......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,如您所见。为了在 x64(但可能不是 x86)上实现最高性能,可能需要改用 cpblk 方法。memcpy
评论
string.Join
所有这些事情都已经为你做了。没有必要自己写。它计算最终字符串的大小,构造该大小的字符串,然后写出到基础字符数组。它甚至具有在此过程中使用可读变量名称的好处。
String.Join
String.Join
Char
String.Join
String.Empty
Concat
String.Join
String.Join
另一种解决方案:
在循环中,使用 List 而不是字符串。
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)
评论
StringBuilder
String.Format
+
String.Concat
StringBuilder
$
StringBuilder
String.Join
+
String.Concat
String.Join
String.Join
String.Join
String.Concat
StringBuilder
String.Join
+
String.Concat
String.Join
StringBuilder
String.Join
$
String.Join