以指定分隔符串聯字符串

對於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}");
        }

測試結果:

類別 循環次數 開始時間 結束時間 Ticks差
Action<T,TextWriter> 10000 2018-11-05 22:13:58:619431 2018-11-05 22:14:01:708661 30862597
Action<T> 10000 2018-11-05 22:30:01:316414 2018-11-05 22:30:04:540565 32241503
嵌套for 10000 2018-11-05 22:48:20:351136 2018-11-05 22:48:23:832171 34810345
Action<T,TextWriter> 100000 2018-11-05 22:17:45:756623 2018-11-05 22:18:14:361243 286046197
Action<T> 100000 2018-11-05 22:30:38:917299 2018-11-05 22:31:08:680268 297629687
嵌套for 100000 2018-11-05 22:50:11:315021 2018-11-05 22:50:45:257604 339425834
Action<T,TextWriter> 1000000 2018-11-05 22:19:02:489665 2018-11-05 22:24:06:528472 3040388071
Action<T> 1000000 2018-11-05 22:32:09:871650 2018-11-05 22:37:05:125522 2952538718
嵌套for 1000000 2018-11-05 22:51:18:319466 2018-11-05 22:56:46:534431 3282149643

從結果中可以看出,性能差距並不大。當然,由於嵌套For循環裏使用索引來尋值,所以可能存在誤差。
如果有錯誤或者更好的方法,歡迎指正。
題外話,方法命名爲Join,可能與Linq的Join存在歧義,建議使用時換成其他名稱。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章