字符串拼接:语言C#(CSharp)

今天我们来介绍一下C#语言的字符串花式拼接方式

1. 基本使用

废话少说,我们直接上代码,介绍一下字符串的拼接的方式

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpPrimeNumber
{
    public class StringSplicingDemo
    {
        public void RunDemo()
        {
            Console.WriteLine("---------------------------------基本使用---------------------------------");
            Console.WriteLine("PlusSignString:" + PlusSignString("小蔡同学", "非常帅"));
            Console.WriteLine("StringFormatString:" + StringFormatString("小蔡同学", "非常帅"));
            Console.WriteLine("StringBuilderString:" + StringBuilderString("小蔡同学", "非常帅"));
            Console.WriteLine("StringFormatVS2017:" + StringFormatVS2017("小蔡同学", "非常帅"));

            Console.WriteLine("--------------------------------------------------------------------------");
        }

        /// <summary>
        /// +号运算
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string PlusSignString(string str1,string str2)
        {
            string strReturn = str1 + str2;
            return strReturn;
        }

        /// <summary>
        /// stringFormat拼接字符串
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string StringFormatString(string str1, string str2)
        {
            string strReturn = string.Format("{0}{1}",str1,str2);
            return strReturn;
        }

        /// <summary>
        /// StringBuilder拼接字符串
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string StringBuilderString(string str1, string str2)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(str1);
            stringBuilder.Append(str2);
            return stringBuilder.ToString();
        }

        /// <summary>
        /// VS2017以上提供的字符串拼接
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string StringFormatVS2017(string str1, string str2)
        {
            string strReturn = string.Format($"{str1}{str2}");
            return strReturn;
        }
    }
    
    static void Main(string[] args)
    {
        StringSplicingDemo pStringSplicingDemo = new StringSplicingDemo();
        pStringSplicingDemo.RunDemo();

        Console.ReadKey();
    }

}

字符串拼接:语言C#(CSharp)

好了,到这里,今天的博客就水到这里了

哈哈哈,各位看客老爷别走,还没有分析完。我们继续说一下以上这几种方式的适用场景。

2. 具体分析

2.1. 算法效率

我们建立几个测试数据,我们将他们拼接,看看效率差距有多大

  • 简单拼接n次,将str1="小菜同学"str2="非常帅",进行一定次数的拼接
  • 复杂拼接n次,将滕王阁序节选拆分后拼接,进行一定次数的拼接

先看结果

---------------------------------1.基本使用---------------------------------
PlusSignString:小蔡同学非常帅
StringFormatString:小蔡同学非常帅
StringBuilderString:小蔡同学非常帅
StringFormatVS2017:小蔡同学非常帅
--------------------------------------------------------------------------
---------------------------------2.算法效率---------------------------------
---------------------------------2.1.简单拼接---------------------------------
---------------------------------2.1.1.简单拼接10000次---------------------------------
PlusSignString:计算10000总共花费1.5047 ms.
StringFormatString:计算10000总共花费3.3441 ms.
StringBuilderString:计算10000总共花费2.7871 ms.
SingletonStringBuilderString:计算10000总共花费2.6882 ms.
StringFormatVS2017:计算10000总共花费1.5376 ms.
--------------------------------------------------------------------------
---------------------------------2.1.2.简单拼接100000次---------------------------------
PlusSignString:计算100000总共花费10.0948 ms.
StringFormatString:计算100000总共花费34.3649 ms.
StringBuilderString:计算100000总共花费16.5996 ms.
SingletonStringBuilderString:计算100000总共花费17.6449 ms.
StringFormatVS2017:计算100000总共花费62.0455 ms.
---------------------------------2.1.2.简单拼接100000次---------------------------------
PlusSignString:计算1000000总共花费117.9943 ms.
StringFormatString:计算1000000总共花费216.8216 ms.
StringBuilderString:计算1000000总共花费170.7078 ms.
SingletonStringBuilderString:计算1000000总共花费103.9131 ms.
StringFormatVS2017:计算1000000总共花费226.8915 ms.
--------------------------------------------------------------------------
--------------------------------------------------------------------------
---------------------------------2.2.复杂拼接---------------------------------
---------------------------------2.2.1.复杂拼接10000次---------------------------------
披绣闼,俯雕甍,山原旷其盈视,川泽纡其骇瞩,闾阎扑地,钟鸣鼎食之家,舸舰弥津,青雀黄龙之舳,云销雨霁,彩彻区明,落霞与孤鹜齐飞,秋水共长天一色,渔舟唱晚,响穷彭蠡之滨,雁阵惊寒,声断衡阳之浦,遥襟甫畅,逸兴遄飞,爽籁发而清风生,纤歌凝而白云遏,睢园绿竹,气凌彭泽之樽,邺水朱华,光照临川之笔,四美具,二难并,穷睇眄于中天,极娱游于暇日,天高地迥,觉宇宙之无穷,兴尽悲来,识盈虚之有数,望长安于日下,目吴会于云间,地势极而南溟深,天柱高而北辰远,关山难越,谁悲失路之人,萍水相逢,尽是他乡之客,怀帝阍而不见,奉宣室以 何年,嗟乎,时运不齐,命途多舛,冯唐易老,李广难封,屈贾谊于长沙,非无圣主,窜梁鸿于海曲,岂乏明时,所赖君子见机,达人知命,老当益壮,宁移白首之心,穷且益坚,不坠青云之志,酌贪泉而觉爽,处涸辙以犹欢,北海虽赊,扶摇可接,东隅已逝,桑榆非晚,孟尝高洁,空余报国之情,阮籍猖狂,岂效穷途之哭。
PlusSignString:计算10000总共花费107.0751 ms.
StringFormatString:计算10000总共花费211.6322 ms.
StringBuilderString:计算10000总共花费24.7743 ms.
SingletonStringBuilderString:计算10000总共花费13.1791 ms.
StringFormatVS2017:计算10000总共花费667.0529 ms.
--------------------------------------------------------------------------
---------------------------------2.2.2.复杂拼接100000次---------------------------------
PlusSignString:计算100000总共花费708.8968 ms.
StringFormatString:计算100000总共花费1498.7978 ms.
StringBuilderString:计算100000总共花费181.1703 ms.
SingletonStringBuilderString:计算100000总共花费103.6469 ms.
StringFormatVS2017:计算100000总共花费4302.9795 ms.
--------------------------------------------------------------------------

其次结论

  • 当简单拼接时,+的方式是最优的
  • 当复杂拼接时,StringBuilder的方式最优
  • 至于string.Format代码的可读性和维护性高一些

再说原因

  • 简单拼接时,StringBuilder生成实例的方式不划算,且需要对对象进行一定操作清理,如使用SingletonStringBuilderString单例StringBuilder
  • 复杂拼接时,则是由于string类型具有不可变性,在底层的存储方式是采用享元的方式存储的,对string字符串的操作(如拼接、Trim()等)都会在内存中产生一个新的字符串对象,在对字符串进行频繁修改的情况下,如在For循环中进行操作等,那么将会频繁的创建新的字符串对象,造成系统的不必要开销,所以这种情况下大家都推荐使用StringBuilder类来对字符串进行操作,那么到底是怎么实现的呢?可以参见下方代码
    /// <summary>
    /// Appends a string to the end of this builder.
    /// </summary>
    /// <param name="value">The string to append.</param>
    public StringBuilder Append(string? value)
    {
        if (value != null)
        {
            // We could have just called AppendHelper here; this is a hand-specialization of that code.
            char[] chunkChars = m_ChunkChars;
            int chunkLength = m_ChunkLength;
            int valueLen = value.Length;

            if (((uint)chunkLength + (uint)valueLen) < (uint)chunkChars.Length) // Use strictly < to avoid issues if count == 0, newIndex == length
            {
                if (valueLen <= 2)
                {
                    if (valueLen > 0)
                    {
                        chunkChars[chunkLength] = value[0];
                    }
                    if (valueLen > 1)
                    {
                        chunkChars[chunkLength + 1] = value[1];
                    }
                }
                else
                {
                    Buffer.Memmove(
                        ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(chunkChars), chunkLength),
                        ref value.GetRawStringData(),
                        (nuint)valueLen);
                }

                m_ChunkLength = chunkLength + valueLen;
            }
            else
            {
                AppendHelper(value);
            }
        }

        return this;
    }

最后贴代码

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CSharpPrimeNumber
{
    public class StringSplicingDemo
    {
        public void RunDemo()
        {
            Console.WriteLine("---------------------------------1.基本使用---------------------------------");
            Console.WriteLine("PlusSignString:" + PlusSignString("小蔡同学", "非常帅"));
            Console.WriteLine("StringFormatString:" + StringFormatString("小蔡同学", "非常帅"));
            Console.WriteLine("StringBuilderString:" + StringBuilderString("小蔡同学", "非常帅"));
            Console.WriteLine("StringFormatVS2017:" + StringFormatVS2017("小蔡同学", "非常帅"));
            Console.WriteLine("--------------------------------------------------------------------------");


            Console.WriteLine("---------------------------------2.算法效率---------------------------------");
            Console.WriteLine("---------------------------------2.1.简单拼接---------------------------------");
            Console.WriteLine("---------------------------------2.1.1.简单拼接10000次---------------------------------");
            SimpleTogether(10000, PlusSignString);
            SimpleTogether(10000, StringFormatString);
            SimpleTogether(10000, StringBuilderString);
            SimpleTogether(10000, SingletonStringBuilderString);
            SimpleTogether(10000, StringFormatVS2017);
            Console.WriteLine("--------------------------------------------------------------------------");

            Console.WriteLine("---------------------------------2.1.2.简单拼接100000次---------------------------------");
            SimpleTogether(100000, PlusSignString);
            SimpleTogether(100000, StringFormatString);
            SimpleTogether(100000, StringBuilderString);
            SimpleTogether(100000, SingletonStringBuilderString);
            SimpleTogether(100000, StringFormatVS2017);

            Console.WriteLine("---------------------------------2.1.2.简单拼接100000次---------------------------------");
            SimpleTogether(1000000, PlusSignString);
            SimpleTogether(1000000, StringFormatString);
            SimpleTogether(1000000, StringBuilderString);
            SimpleTogether(1000000, SingletonStringBuilderString);
            SimpleTogether(1000000, StringFormatVS2017);
            Console.WriteLine("--------------------------------------------------------------------------");
            Console.WriteLine("--------------------------------------------------------------------------");

            Console.WriteLine("---------------------------------2.2.复杂拼接---------------------------------");
            Console.WriteLine("---------------------------------2.2.1.复杂拼接10000次---------------------------------");

            List<string> listInfo = new List<string>() {"披绣闼","俯雕甍","山原旷其盈视","川泽纡其骇瞩","闾阎扑地","钟鸣鼎食之家","舸舰弥津","青雀黄龙之舳","云销雨霁","彩彻区明","落霞与孤鹜齐飞","秋水共长天一色","渔舟唱晚","响穷彭蠡之滨","雁阵惊寒","声断衡阳之浦",
                "遥襟甫畅","逸兴遄飞","爽籁发而清风生","纤歌凝而白云遏","睢园绿竹","气凌彭泽之樽","邺水朱华","光照临川之笔","四美具","二难并","穷睇眄于中天","极娱游于暇日","天高地迥","觉宇宙之无穷","兴尽悲来","识盈虚之有数","望长安于日下","目吴会于云间","地势极而南溟深","天柱高而北辰远","关山难越","谁悲失路之人","萍水相逢","尽是他乡之客",
                "怀帝阍而不见","奉宣室以何年",
                "嗟乎","时运不齐","命途多舛","冯唐易老","李广难封","屈贾谊于长沙","非无圣主","窜梁鸿于海曲","岂乏明时",
                "所赖君子见机","达人知命","老当益壮","宁移白首之心","穷且益坚","不坠青云之志","酌贪泉而觉爽","处涸辙以犹欢","北海虽赊","扶摇可接","东隅已逝","桑榆非晚","孟尝高洁","空余报国之情","阮籍猖狂","岂效穷途之哭" };

            Console.WriteLine(string.Join(",", listInfo) + "。");

            ComplexTogether(10000, listInfo, PlusSignString);
            ComplexTogether(10000,listInfo, StringFormatString); 
            ComplexTogether(10000, listInfo, StringBuilderString);
            ComplexTogether(10000, listInfo, SingletonStringBuilderString);
            ComplexTogether(10000, listInfo, StringFormatVS2017);
            Console.WriteLine("--------------------------------------------------------------------------");

            Console.WriteLine("---------------------------------2.2.2.复杂拼接100000次---------------------------------");
            ComplexTogether(100000,listInfo, PlusSignString);
            ComplexTogether(100000,listInfo, StringFormatString);
            ComplexTogether(100000,listInfo, StringBuilderString);
            ComplexTogether(100000, listInfo, SingletonStringBuilderString);
            ComplexTogether(100000, listInfo, StringFormatVS2017);
            Console.WriteLine("--------------------------------------------------------------------------");
        }

        StringBuilder SingletonStringBuilder = new StringBuilder();

        #region 复杂字符串拼接代码段
        /// <summary>
        /// 复杂字符串拼接
        /// </summary>
        /// <param name="num"></param>
        /// <param name="list"></param>
        /// <param name="action"></param>
        public void ComplexTogether(int num, List<string> list, Func<List<string>, string> action)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < num; i++)
            {
                action(list);
            }
            sw.Stop();
            TimeSpan ts2 = sw.Elapsed;
            Console.WriteLine($"{action.Method.Name}:计算{num}总共花费{ts2.TotalMilliseconds} ms.");
        }

        public string PlusSignString(List<string> listInfo)
        {
            string strReturn = "";
            for (int i = 0; i < listInfo.Count; i++)
            {
                strReturn += listInfo[i];
            }
            return strReturn;
        }

        public string StringFormatString(List<string> listInfo)
        {
            string strReturn = "";
            for (int i = 0; i < listInfo.Count; i++)
            {
                strReturn =string.Format("{0}{1}",strReturn,listInfo[i]);
            }
            return strReturn;
        }


        public string StringBuilderString(List<string> listInfo)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (var item in listInfo)
            {
                stringBuilder.Append(item);
            }
            return stringBuilder.ToString();
        }

        /// <summary>
        /// StringBuilder拼接字符串
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string SingletonStringBuilderString(List<string> listInfo)
        {
            SingletonStringBuilder.Clear();
            foreach (var item in listInfo)
            {
                SingletonStringBuilder.Append(item);
            }
            return SingletonStringBuilder.ToString();
        }

        public string StringFormatVS2017(List<string> listInfo)
        {
            string strReturn = "";
            for (int i = 0; i < listInfo.Count; i++)
            {
                strReturn = string.Format($"{strReturn}{listInfo[i]}");
            }
            return strReturn;
        }
        #endregion

        #region 简单字符串拼接代码段
        /// <summary>
        /// 简单字符串拼接
        /// </summary>
        /// <param name="num"></param>
        /// <param name="action"></param>
        public void SimpleTogether(int num, Func<string,string, string> action)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < num; i++)
            {
                action("小蔡同学", "非常帅"+i);
            }
            sw.Stop();
            TimeSpan ts2 = sw.Elapsed;
            Console.WriteLine($"{action.Method.Name}:计算{num}总共花费{ts2.TotalMilliseconds} ms.");
        }

        /// <summary>
        /// +号运算
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string PlusSignString(string str1,string str2)
        {
            string strReturn = str1 + str2;
            return strReturn;
        }

        /// <summary>
        /// stringFormat拼接字符串
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string StringFormatString(string str1, string str2)
        {
            string strReturn = string.Format("{0}{1}",str1,str2);
            return strReturn;
        }

        /// <summary>
        /// StringBuilder拼接字符串
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string SingletonStringBuilderString(string str1, string str2)
        {
            SingletonStringBuilder.Clear();
            SingletonStringBuilder.Append(str1);
            SingletonStringBuilder.Append(str2);
            return SingletonStringBuilder.ToString();
        }

        /// <summary>
        /// StringBuilder拼接字符串
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string StringBuilderString(string str1, string str2)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(str1);
            stringBuilder.Append(str2);
            return stringBuilder.ToString();
        }

        /// <summary>
        /// VS2017以上提供的字符串拼接
        /// </summary>
        /// <param name="str1"></param>
        /// <param name="str2"></param>
        /// <returns></returns>
        public string StringFormatVS2017(string str1, string str2)
        {
            string strReturn = string.Format($"{str1}{str2}");
            return strReturn;
        }
        #endregion
    }
}

2.2 代码可读性

  1. 简单情况下+运算,是最清晰的
  2. 极度复杂且重复的情况下StringBuilder是最佳的方式
  3. 当只是拼接一次长字符串是,我认为string.format可读性是最强的
  • string.Format("{0}",str) 和 string.Format($"{str}")

    • string.Format($"{str}") 我非常推崇这种写法

举例说明:现在有一个如下代码,使用string.Format("{0}",str)

string strUpdateSql=string.Format("update {0} set FieldA='{1}' where ID ={2}",TableName,FieldAValuem,IDValue);

以上代码是拼接一个sql语句,这样相较+StringBuilder更加具有优势。

如果这个strUpdateSql不发生改变就还好,但是如果发生改变,如现在需要多更新几个字段,代码转变为如下:几种情况

string strUpdateSql=string.Format("update {0} set FieldA='{1}',FieldA='{2}' where ID ={3}",TableName,FieldAValuem,FieldBValuem,IDValue);
string strUpdateSql=string.Format("update {0} set FieldA='{1}',FieldA='{3}' where ID ={2}",TableName,FieldAValuem,IDValue,FieldBValuem);

如果仔细思考的话,我们应该想到:当字段复杂的时候上述两种拼接都是一个毫无必要的拼接,第一种情况:遵循强迫症或者说为了代码有一个从始至终的编码逻辑,就需要调整,第二种情况:可以预想到当经过几轮的没有原则的修改,这个长代码的维护性及其的差

一下我们贴一下+的代码,仔细对比一下,个人认为在代码的引号上非常容易出错

string strUpdateSql="update "+TableName+" set FieldA='"+FieldAValuem+"',FieldA='"+FieldBValuem+"' where ID ="+IDValue;

在这里我们写一下string.Format($"{str}")

string strUpdateSql=string.Format($"update {TableName} set FieldA='{FieldAValuem}',FieldA='{FieldBValuem}' where ID ={IDValue}");

以上代码的可读性以及维护性及其的好。

但是,凡是都担心但是,这种写法是在VS2017之后出现的,这种写法是编译器行为,如果需要在低版本VS中继续编写代码时,就不建议使用这种代码编写格式了。

3. 小技巧

我个人还是看了蛮多上古代码的,其中有一种代码我见过很多,现在有三个人名,需要用、分隔拼接起来,大部分代码如下方式。


List<string> listName=new List<string>(){"张三","李四","王五"};

string strReturn=listName[0];

for(int i=1;i<listName.Count;i++)
{
    strReturn+="、"+listName[0];
}

// string strReturn="";
// for(int i=0;i<listName.Count;i++)
// {
//     strReturn+="、"+listName[0];
// }
// strRetuern=strReturn==""?strReturn:strReturn.SubString(1);

return strReturn;

个人的写法如下:


List<string> listName=new List<string>(){"张三","李四","王五"};

string strReturn=string.Join("、",listName);

C#源码如下:mscorlib_Source\System\String.cs

[ComVisible(false)]
public static string Join(string separator, IEnumerable<string> values)
{
    if (values == null)
    {
        throw new ArgumentNullException("values");
    }
    if (separator == null)
    {
        separator = Empty;
    }
    using (IEnumerator<string> enumerator = values.GetEnumerator())
    {
        if (!enumerator.MoveNext())
        {
            return Empty;
        }
        StringBuilder builder = new StringBuilder();
        if (enumerator.Current != null)
        {
            builder.Append(enumerator.Current);
        }
        while (enumerator.MoveNext())
        {
            builder.Append(separator);
            if (enumerator.Current != null)
            {
                builder.Append(enumerator.Current);
            }
        }
        return builder.ToString();
    }
}

我个人写代码和改代码都相较蛮快的,是因为在每一个细节上都考虑可读性、简洁、是不是常人思路以及这个地方如果改怎么方便改

上一篇:计算机二级《MS Office》考试真题及答案


下一篇:【图像识别】基于卷积神经网络实现英文字母及单词识别matlab代码