使用LINQ来连接string

什么是最有效的写老派的方法:

StringBuilder sb = new StringBuilder(); if (strings.Count > 0) { foreach (string s in strings) { sb.Append(s + ", "); } sb.Remove(sb.Length - 2, 2); } return sb.ToString(); 

…在LINQ?

使用像这样的聚合查询:

 string[] words = { "one", "two", "three" }; var res = words.Aggregate((current, next) => current + ", " + next); Console.WriteLine(res); 

这输出:

 一二三 

聚合是一个函数,它接收一个值的集合并返回一个标量值。 来自T-SQL的示例包括最小值,最大值和总和。 VB和C#都支持聚合。 VB和C#都支持聚合作为扩展方法。 使用点符号,只需调用IEnumerable对象上的方法即可。

请记住,聚合查询是立即执行的。

http://msdn.microsoft.com/en-us/library/bb386914.aspx

因为这不使用一个StringBuilder它将有非常长的序列可怕的performance。

 return string.Join(", ", strings.ToArray()); 

在.Net 4中, string.Join有一个新的重载 ,它接受IEnumerable<string> 。 代码将如下所示:

 return string.Join(", ", strings); 

为什么要使用Linq?

 string[] s = {"foo", "bar", "baz"}; Console.WriteLine(String.Join(", ", s)); 

这工作完美,并接受任何IEnumerable<string>据我记得。 没有必要在这里Aggregate任何慢得多的东西。

你看过聚合扩展方法吗?

 var sa = (new[] { "yabba", "dabba", "doo" }).Aggregate((a,b) => a + "," + b); 

来自我的代码的真实例子:

 return selected.Select(query => query.Name).Aggregate((a, b) => a + ", " + b); 

查询是一个具有Name属性的对象,它是一个string,我希望所选列表中的所有查询的名称以逗号分隔。

你可以在Aggregate使用StringBuilder

  List<string> strings = new List<string>() { "one", "two", "three" }; StringBuilder sb = strings .Select(s => s) .Aggregate(new StringBuilder(), (ag, n) => ag.Append(n).Append(", ")); if (sb.Length > 0) { sb.Remove(sb.Length - 2, 2); } Console.WriteLine(sb.ToString()); 

Select在那里只是为了显示你可以做更多的LINQ的东西。)

由于这个问题在几个星期前曾经有过一些活动,所以我决定在查看上面的答案和对类似问题的答案中提到的问题之后,抛出我所解决的Join / Linq方法是合适的有0个元素的聚合和连接失败)。

string Result = String.Join(",", split.Select(s => s.Name));

或者(如果s不是一个string)

string Result = String.Join(",", split.Select(s => s.ToString()));

  • 简单
  • 容易阅读和理解
  • 适用于通用元素
  • 允许使用对象或对象属性
  • 处理0长度元素的情况
  • 可以使用额外的Linq过滤
  • performance不错(至less以我的经验)
  • 不需要(手动)创build一个额外的对象(如StringBuilder )来实现

当然,join会照顾到有时会偷偷摸摸地进入其他方法的讨厌的最后的逗号,这就是为什么我首先寻找Linq-y解决scheme的原因。

当然,如果有人看到这个方法有什么问题,我很乐意采纳他们可能提出的任何build议或改进。

stingbuilder快速性能数据和超过3000个元素的select案例:

unit testing持续时间(秒)LINQ_SELECT 00:00:01.8012535
LINQ_StringBuilder 00:00:00.0036644

  [TestMethod()] public void LINQ_StringBuilder() { IList<int> ints = new List<int>(); for (int i = 0; i < 3000;i++ ) { ints.Add(i); } StringBuilder idString = new StringBuilder(); foreach (int id in ints) { idString.Append(id + ", "); } } [TestMethod()] public void LINQ_SELECT() { IList<int> ints = new List<int>(); for (int i = 0; i < 3000; i++) { ints.Add(i); } string ids = ints.Select(query => query.ToString()).Aggregate((a, b) => a + ", " + b); } 

我总是使用扩展方法:

 public static string JoinAsString<T>(this IEnumerable<T> input, string seperator) { var ar = input.Select(i => i.ToString()).ToArray(); return string.Join(seperator, ar); } 

通过“ 超酷的LINQ方式 ”,您可能正在讨论LINQ使得function性编程更适合使用扩展方法的方式。 我的意思是,语法糖允许函数以视觉线性方式(一个接一个地)链接而不是嵌套(一个在另一个之内)。 例如:

 int totalEven = Enumerable.Sum(Enumerable.Where(myInts, i => i % 2 == 0)); 

可以这样写:

 int totalEven = myInts.Where(i => i % 2 == 0).Sum(); 

你可以看到第二个例子更容易阅读。 您还可以看到如何使用更less的缩进问题添加更多的函数,或者在expression式结尾处显示Lispy最后一个closures的零部件

其他许多答案指出, String.Join是最好的select,因为它是最快或最简单的阅读。 但是,如果你把我对“ 超酷的LINQ方式 ”的解释,那么答案就是使用String.Join但是它包装在一个LINQ风格的扩展方法中,这将允许你以一种视觉愉悦的方式链接你的函数。 所以如果你想写sa.Concatenate(", ")你只需要创build这样的东西:

 public static class EnumerableStringExtensions { public static string Concatenate(this IEnumerable<string> strings, string separator) { return String.Join(separator, strings); } } 

这将提供与直接调用一样的代码(至less在algorithm复杂度方面),在某些情况下可能会使代码更具可读性(取决于上下文),特别是如果块中的其他代码使用链接函数样式。

在上一个问题中有各种不同的答案 – 无可否认,它将整数数组作为源,但是得到了一般化的答案。

这里使用纯LINQ作为单个expression式:

 static string StringJoin(string sep, IEnumerable<string> strings) { return strings .Skip(1) .Aggregate( new StringBuilder().Append(strings.FirstOrDefault() ?? ""), (sb, x) => sb.Append(sep).Append(x)); } 

而其相当该死的速度!

我会稍微作弊,然后抛出一个新的答案,似乎总结了这里所有的最好的东西,而不是把它放在评论里面。

所以你可以这样做:

 List<string> strings = new List<string>() { "one", "two", "three" }; string concat = strings .Aggregate(new StringBuilder("\a"), (current, next) => current.Append(", ").Append(next)) .ToString() .Replace("\a, ",string.Empty); 

编辑:你要么要先检查一个空的枚举或添加一个.Replace("\a",string.Empty); 到expression式的结尾。 猜猜我可能一直在努力变得有些过于聪明。

来自@ a.friend的答案可能会稍微有点高性能,我不确定相比于Remove,引擎盖下面是什么。 唯一的另一个警告,如果某种原因,你想连接string,结束在\ A的你会失去你的分隔符…我觉得不太可能。 如果是这样的话,你确实有其他奇特的angular色可供select。

你可以很有效地结合使用LINQ和string.join() 。 在这里,我从string中删除一个项目。 还有更好的方法做到这一点,但这里是:

 filterset = String.Join(",", filterset.Split(',') .Where(f => mycomplicatedMatch(f,paramToMatch)) ); 

这里有很多select。 你可以使用LINQ和一个StringBuilder来获得性能,就像这样:

 StringBuilder builder = new StringBuilder(); List<string> MyList = new List<string>() {"one","two","three"}; MyList.ForEach(w => builder.Append(builder.Length > 0 ? ", " + w : w)); return builder.ToString(); 

我在使用linqparsingIIS日志文件时做了以下快速和肮脏的操作,它在@ 100万行很好(15秒)的工作,虽然尝试200万行时出现内存不足错误。

  static void Main(string[] args) { Debug.WriteLine(DateTime.Now.ToString() + " entering main"); // USED THIS DOS COMMAND TO GET ALL THE DAILY FILES INTO A SINGLE FILE: copy *.log target.log string[] lines = File.ReadAllLines(@"C:\Log File Analysis\12-8 E5.log"); Debug.WriteLine(lines.Count().ToString()); string[] a = lines.Where(x => !x.StartsWith("#Software:") && !x.StartsWith("#Version:") && !x.StartsWith("#Date:") && !x.StartsWith("#Fields:") && !x.Contains("_vti_") && !x.Contains("/c$") && !x.Contains("/favicon.ico") && !x.Contains("/ - 80") ).ToArray(); Debug.WriteLine(a.Count().ToString()); string[] b = a .Select(l => l.Split(' ')) .Select(words => string.Join(",", words)) .ToArray() ; System.IO.File.WriteAllLines(@"C:\Log File Analysis\12-8 E5.csv", b); Debug.WriteLine(DateTime.Now.ToString() + " leaving main"); } 

我以前使用linq的真正原因是一个Distinct()我需要:

 string[] b = a .Select(l => l.Split(' ')) .Where(l => l.Length > 11) .Select(words => string.Format("{0},{1}", words[6].ToUpper(), // virtual dir / service words[10]) // client ip ).Distinct().ToArray() ; 

我前段时间在博客上写道,我做了什么接缝,正是你要找的东西:

http://ondevelopment.blogspot.com/2009/02/string-concatenation-made-easy.html

在博客文章中描述了如何实现扩展方法在IEnumerable上工作,并命名为Concatenate,这样可以让你编写如下的东西:

 var sequence = new string[] { "foo", "bar" }; string result = sequence.Concatenate(); 

或者更复杂的东西,如:

 var methodNames = typeof(IFoo).GetMethods().Select(x => x.Name); string result = methodNames.Concatenate(", ");