以指定分隔符串联字符串
对于String.Join(String, String[])这个方法,相信大家都有所了解。唯一不足的就是这个方法只能用于字符串数组,适用场景比较少。我所遇到的场景中,更多的是对List<T>中的某属性进行串联。尚未发现已实现该功能的方法,于是自己实现了该功能。
实现的方式是对IEnumerable<T>进行扩展。
/// <summary> /// 将数组合并成字符串,Action<<typeparamref name="T"/>,TextWriter> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="enumerable"></param> /// <param name="separator"></param> /// <param name="action"></param> /// <param name="output"></param> public static void Join<T>(this IEnumerable<T> enumerable,String separator,Action<T,TextWriter> action,TextWriter output) { Func<TextWriter, String,Boolean> writeSp = (x, y) => { x.Write(y); return true; }; IEnumerator<T> en = enumerable.GetEnumerator(); if(en.MoveNext()) { do { action(en.Current,output); } while(en.MoveNext() && writeSp(output, separator)); } } /// <summary> /// 将数组合并成字符串,Action<<typeparamref name="T"/>> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="enumerable"></param> /// <param name="separator"></param> /// <param name="action"></param> /// <param name="output"></param> public static void Join<T>(this IEnumerable<T> enumerable,String separator,Action<T> action,TextWriter output) { Func<TextWriter, String,Boolean> writeSp = (x, y) => { x.Write(y); return true; }; IEnumerator<T> en = enumerable.GetEnumerator(); if(en.MoveNext()) { do { action(en.Current); } while(en.MoveNext() && writeSp(output, separator)); } }
下面通过实现“DataTable转CSV”来对这两个方法和嵌套For循环进行对比。
在这个Demo里,DataTable有3列100行数据。对3种实现方式分别进行10000、100000和1000000循环。
Demo:
[TestMethod] public void TestMethod5() { DataTable dt = new DataTable("1234"); dt.Columns.Add("A"); dt.Columns.Add("B"); dt.Columns.Add("C"); for(int i = 0; i < 100; i++) { dt.Rows.Add("A" + i, "B" + i, "C" + i); } StringBuilder sb;//= new StringBuilder(); StringWriter sw;// = new StringWriter(sb); String rowSeparator = "\n"; String columnSeparator = ","; //Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff")); DateTime time1 = DateTime.Now; Int32 count = 1000000; for(int c = 0; c < count; c++) { sb = new StringBuilder(); sw = new StringWriter(sb); #region Action<T,TextWriter> // var columns = dt.Columns.OfType<DataColumn>(); //// 写标题 //columns.Join(columnSeparator, (x) => x.ColumnName, sw); //sw.Write(rowSeparator); //// 写数据 //dt.Select().Join(rowSeparator, (x, x1) => columns.Join(columnSeparator, (y, y1) => { y1.Write('"'); y1.Write(x[y]); y1.Write('"'); }, x1), sw); #endregion #region Action<T> // var columns = dt.Columns.OfType<DataColumn>(); //columns.Join(columnSeparator, (x) => sw.Write(x.ColumnName), sw); //sw.Write(rowSeparator); //dt.Select().Join(rowSeparator, (x) => columns.Join(columnSeparator, (y) => { sw.Write('"'); sw.Write(x[y]); sw.Write('"'); }, sw), sw); #endregion #region 嵌套for Func<TextWriter, String, Boolean> func = (x, y) => { x.Write(y); return true; }; sb = new StringBuilder(); for(int i = 0; i < dt.Columns.Count && func(sw, columnSeparator); i++) { sw.Write(dt.Columns[i].ColumnName); } sw.Write(rowSeparator); for(int i = 0; i < dt.Rows.Count && func(sw, rowSeparator); i++) { for(int j = 0; j < dt.Columns.Count && func(sw, columnSeparator); j++) { sw.Write('"'); sw.Write(dt.Rows[i][j]); sw.Write('"'); } } #endregion sw.Flush(); sw.Close(); //Console.WriteLine(sb.ToString()); } //Console.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss:ffffff")); DateTime time2 = DateTime.Now; Console.WriteLine($"{time1.ToString("yyyy-MM-dd HH:mm:ss:ffffff")}\t{time2.ToString("yyyy-MM-dd HH:mm:ss:ffffff")}\t{time2.Ticks - time1.Ticks}"); }
测试结果:
从结果中可以看出,性能差距并不大。当然,由于嵌套For循环里使用索引来寻值,所以可能存在误差。
如果有错误或者更好的方法,欢迎指正。
题外话,方法命名为Join,可能与Linq的Join存在歧义,建议使用时换成其他名称。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。