【问题标题】:Join a string using delimiters使用分隔符连接字符串
【发布时间】:2025-12-22 04:25:12
【问题描述】:

将字符串列表加入组合分隔字符串的最佳方法是什么。我主要关心何时停止添加分隔符。我将使用 C# 作为示例,但我希望它与语言无关。

编辑:我没有使用 StringBuilder 来简化代码。

使用 For 循环

for(int i=0; i < list.Length; i++)
{
    result += list[i];
    if(i != list.Length - 1)
        result += delimiter;
}

使用 For 循环设置之前的第一项

result = list[0];
for(int i = 1; i < list.Length; i++)
    result += delimiter + list[i];

这些不适用于您事先不知道列表长度的 IEnumerable,因此

使用 foreach 循环

bool first = true;
foreach(string item in list)
{
    if(!first)
        result += delimiter;
    result += item;
    first = false;
}

foreach 循环的变化

来自乔恩的解决方案

StringBuilder builder = new StringBuilder();
string delimiter = "";
foreach (string item in list)
{
    builder.Append(delimiter);
    builder.Append(item);
    delimiter = ",";       
}
return builder.ToString();

使用迭代器

再次来自乔恩

using (IEnumerator<string> iterator = list.GetEnumerator())
{
    if (!iterator.MoveNext())
        return "";
    StringBuilder builder = new StringBuilder(iterator.Current);
    while (iterator.MoveNext())
    {
        builder.Append(delimiter);
        builder.Append(iterator.Current);
    }
    return builder.ToString();
}

还有哪些其他算法?

【问题讨论】:

  • 别忘了在你的 foreach 循环中设置 first 为 false
  • 如果您希望它与语言无关,您不必担心特定于 c# 的优化 (stringbuilder)。

标签: algorithm language-agnostic string


【解决方案1】:

这里不可能给出真正与语言无关的答案,因为不同的语言和平台以不同的方式处理字符串,并为加入字符串列表提供不同级别的内置支持。您可以使用两种不同的语言编写几乎相同的代码,其中一种语言会很棒,而另一种语言会很糟糕。

在 C# 中,您可以使用:

StringBuilder builder = new StringBuilder();
string delimiter = "";
foreach (string item in list)
{
    builder.Append(delimiter);
    builder.Append(item);
    delimiter = ",";       
}
return builder.ToString();

这将在除第一个项目之外的所有项目上附加一个逗号。类似的代码在 Java 中也会很好。

编辑:这是一个替代方案,有点像伊恩后来的回答,但工作的是一般IEnumerable&lt;string&gt;

// Change to IEnumerator for the non-generic IEnumerable
using (IEnumerator<string> iterator = list.GetEnumerator())
{
    if (!iterator.MoveNext())
    {
        return "";
    }
    StringBuilder builder = new StringBuilder(iterator.Current);
    while (iterator.MoveNext())
    {
        builder.Append(delimiter);
        builder.Append(iterator.Current);
    }
    return builder.ToString();
}

在原始答案后近 5 年编辑...

在 .NET 4 中,string.Join 严重超载。 IEnumerable&lt;T&gt; 有一个重载,它会自动调用ToStringIEnumerable&lt;string&gt; 有一个重载。所以你不再需要上面的代码了......无论如何,对于.NET。

【讨论】:

  • 现在我的解决方案比你的还短:-)
  • 较短但效率较低 - 在末尾使用子字符串仍然意味着不雅的副本。
  • 这是一个相当老的话题,但如果你可以使用 StringBuilder,我觉得有必要指出一个不同的解决方案。该类有一个名为 Length 的属性,您可以随意更改它。因此,始终添加分隔符,并在循环之后减少分隔符长度的 Length 属性。但是,性能差异很小
  • 对于 VB.Net,您可以将分隔符添加为 sBuilder.append("text" & ",")。然后当你完成后,sBuilder.toString.TrimEnd(","c).
  • 没错,这取决于具体情况。修剪它的优点是您不必为每个列表项的变量分配一个值,因此对于较大的列表来说它更便宜。这取决于您的应用程序。我主要在 SQL 和 JavaScript 中处理它,所以它很少成为问题。
【解决方案2】:

在 .NET 中,您可以使用String.Join method

string concatenated = String.Join(",", list.ToArray());

使用.NET Reflector,我们可以了解它是如何做到的:

public static unsafe string Join(string separator, string[] value, int startIndex, int count)
{
    if (separator == null)
    {
        separator = Empty;
    }
    if (value == null)
    {
        throw new ArgumentNullException("value");
    }
    if (startIndex < 0)
    {
        throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_StartIndex"));
    }
    if (count < 0)
    {
        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
    }
    if (startIndex > (value.Length - count))
    {
        throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_IndexCountBuffer"));
    }
    if (count == 0)
    {
        return Empty;
    }
    int length = 0;
    int num2 = (startIndex + count) - 1;
    for (int i = startIndex; i <= num2; i++)
    {
        if (value[i] != null)
        {
            length += value[i].Length;
        }
    }
    length += (count - 1) * separator.Length;
    if ((length < 0) || ((length + 1) < 0))
    {
        throw new OutOfMemoryException();
    }
    if (length == 0)
    {
        return Empty;
    }
    string str = FastAllocateString(length);
    fixed (char* chRef = &str.m_firstChar)
    {
        UnSafeCharBuffer buffer = new UnSafeCharBuffer(chRef, length);
        buffer.AppendString(value[startIndex]);
        for (int j = startIndex + 1; j <= num2; j++)
        {
            buffer.AppendString(separator);
            buffer.AppendString(value[j]);
        }
    }
    return str;
}

【讨论】:

  • IEnumerable 的变量不能使用 ToArray。但是通用的可以。
  • @CodeMelt: IEnumerable 可以通过 .NET 3.5 中的 Enumerable.ToArray 扩展方法使用 ToArray。
  • @Jon,我的第一条评论说通用的可以做 ToArray。 IEnumerable 是没有扩展方法的。
  • 好的,我和你在一起。出于兴趣,有人在哪里提到非泛型 IEnumerable?
  • @Jon,在问题中,它询问“这些不适用于您事先不知道列表长度的 IEnumerable”
【解决方案3】:

当某些语言在一行中提供对此的支持时,几乎没有理由让它与语言无关,例如 Python 的

",".join(sequence)

请参阅the join documentation 了解更多信息。

【讨论】:

    【解决方案4】:

    对于 python,请确保您有一个字符串列表,否则 ','.join(x) 将失败。 对于使用 2.5+ 的安全方法

    delimiter = '","'
    delimiter.join(str(a) if a else '' for a in list_object)
    

    "str(a) if a else ''" 适用于 None 类型,否则 str() 最终会生成 then 'None' ;)

    【讨论】:

      【解决方案5】:

      在 PHP 的implode():

      $string = implode($delim, $array);
      

      【讨论】:

        【解决方案6】:

        我总是添加分隔符,然后在必要时将其删除。这样,当您只关心执行一次工作时,就不会为循环的每次迭代都执行 if 语句。

        StringBuilder sb = new StringBuilder();
        
        foreach(string item in list){
            sb.Append(item);
            sb.Append(delimeter);
        }
        
        if (list.Count > 0) {
            sb.Remove(sb.Length - delimter.Length, delimeter.Length)
        }
        

        【讨论】:

          【解决方案7】:

          我会递归地表达这一点。

          • 检查字符串参数个数是否为1,如果是则返回。
          • 否则递归,但将前两个参数与它们之间的分隔符结合起来。

          Common Lisp 中的示例:

          (defun join (分隔符 &rest 字符串) (如果(空(其余字符串)) (第一个字符串) (申请#'加入 分隔符 (连接'字符串 (第一个字符串) 分隔符 (第二个字符串)) (cddr 字符串))))

          更惯用的方法是使用reduce,但这扩展为与上面几乎完全相同的指令:

          (defun join (分隔符 &rest 字符串) (减少 (lambda (a b) (concatenate 'string a delimiter b)) 字符串))

          【讨论】:

            【解决方案8】:
            List<string> aaa = new List<string>{ "aaa", "bbb", "ccc" };
            string mm = ";";
            return aaa.Aggregate((a, b) => a + mm + b);
            

            你得到

            aaa;bbb;ccc
            

            lambda 很方便

            【讨论】:

            • 注意:由于一遍又一遍地复制生成的字符串,这会导致 Schlemiel the Painter 算法。最好使用 StringBuilder 或 string.Join 来使用重载。
            【解决方案9】:

            在 C# 中,您可以只使用 String.Join(separator,string_list)

            【讨论】:

            • IEnumerable的变量不能使用ToArray
            【解决方案10】:

            问题是计算机语言很少有字符串布尔值,也就是说,字符串类型的方法可以做任何有用的事情。 SQL Server至少有is[not]null和nullif,它们结合起来解决分隔符问题,顺便说一句:isnotnull(nullif(columnvalue, ""),"," + columnvalue))

            问题在于,在语言中有布尔值,也有字符串,除了丑陋的编码形式之外,这两者永远不会相遇,例如

            concatstring = string1 + "," + string2; 如果(富巴) 连接字符串 += 字符串 3 concatstring += string4 等

            我已经尽力避免所有这些丑陋,玩逗号游戏和连接连接,但我仍然留下了一些,包括 SQL Server 错误,当我错过了一个逗号和一个变量是空。

            乔纳森

            【讨论】:

              【解决方案11】:

              这就是python解决问题的方式:

              ','.join(list_of_strings)
              

              不过,我一直无法理解在琐碎案例中对“算法”的需求

              【讨论】:

              • 不,这就是您在 Python 中编写它的方式。要了解 Python 如何解决该问题,您必须研究join 的实现,在那里您会找到一种算法。
              • 不,这就是 python 为我解决这个问题的方式。我不需要发明任何算法,因为 python 为我提供了惯用的内置方法。特定的 python 实现使用什么算法在这里无关紧要。
              • 问题在于算法。作为最初的发帖人,我也不会对“我打电话给 Bob,他可以用 Blub 编码”感兴趣。
              • 顺便说一句,我不明白你为什么回滚到语法和拼写错误。这是一个自我问题吗?你现在必须忍受它。
              • 嗯,这是python中的算法。有些语言很愚蠢,这不是我的错。我的语法和拼写没有问题,非常感谢。
              【解决方案12】:

              由于您将此语言标记为不可知论,

              这就是你在 python 中的做法

              # delimiter can be multichar like "| trlalala |"
              delimiter = ";"
              # sequence can be any list, or iterator/generator that returns list of strings
              result = delimiter.join(sequence)
              #result will NOT have ending delimiter 
              

              编辑:我看到我被几个人打败了。抱歉重复了

              【讨论】:

                【解决方案13】:

                我认为最好的方法是(我将使用伪代码,因此我们将使其真正与语言无关):

                function concat(<array> list, <boolean> strict):
                  for i in list:
                    if the length of i is zero and strict is false:
                      continue;
                    if i is not the first element:
                      result = result + separator;
                    result = result + i;
                  return result;
                

                concat() 的第二个参数strict 是一个标志,用于知道是否必须在连接中考虑最终的空字符串。

                我习惯于不考虑附加最后的分隔符;另一方面,如果 strict 为 false,则生成的字符串可能不含“A,B,,,F”之类的内容,前提是分隔符是逗号,而是显示为“A,B,F”。

                【讨论】:

                  【解决方案14】:

                  这是 C# 中的有效解决方案,在 Java 中,您可以对每个迭代器使用类似的解决方案。

                          string result = string.Empty; 
                  
                          // use stringbuilder at some stage.
                          foreach (string item in list)
                              result += "," + item ;
                  
                          result = result.Substring(1);
                          // output:  "item,item,item"
                  

                  如果使用 .NET,您可能需要使用扩展方法,这样您就可以做到 list.ToString(",") 详情请查看Separator Delimited ToString for Array, List, Dictionary, Generic IEnumerable

                  // contains extension methods, it must be a static class.
                  public static class ExtensionMethod
                  {
                      // apply this extension to any generic IEnumerable object.
                      public static string ToString<T>(this IEnumerable<T> source,
                        string separator)
                      {
                          if (source == null)
                             throw new ArgumentException("source can not be null.");
                  
                          if (string.IsNullOrEmpty(separator))
                             throw new ArgumentException("separator can not be null or empty.");
                  
                          // A LINQ query to call ToString on each elements
                          // and constructs a string array.
                          string[] array =
                           (from s in source
                            select s.ToString()
                            ).ToArray();
                  
                          // utilise builtin string.Join to concate elements with
                          // customizable separator.
                          return string.Join(separator, array);
                      }
                  }
                  

                  编辑:出于性能原因,将连接代码替换为该线程中提到的字符串生成器解决方案。

                  【讨论】:

                  • 像第一个例子一样使用字符串连接是非常低效的。在 Java 和 .NET 中,使用 StringBuilder。
                  • @Jon,顺便说一句,第一个例子上面有评论。
                  【解决方案15】:

                  看到 Python 的答案 3 次,但没有 Ruby?!?!?

                  代码的第一部分声明了一个新数组。然后你可以调用 .join() 方法并传递分隔符,它会返回一个中间有分隔符的字符串。我相信 join 方法在连接之前会在每个项目上调用 .to_s 方法。

                  ["ID", "Description", "Active"].join(",")
                  >> "ID, Description, Active"
                  

                  这在将元编程与数据库交互相结合时非常有用。

                  有人知道c#是否有类似这种语法糖的东西吗?

                  【讨论】:

                    【解决方案16】:

                    在 Java 8 中我们可以使用:

                    List<String> list = Arrays.asList(new String[] { "a", "b", "c" });
                    System.out.println(String.join(",", list)); //Output: a,b,c
                    

                    我们可以有前缀和后缀

                    StringJoiner joiner = new StringJoiner(",", "{", "}");
                    list.forEach(x -> joiner.add(x));
                    System.out.println(joiner.toString()); //Output: {a,b,c}
                    

                    在 Java 8 之前,您可以像 Jon 的回答那样做

                    StringBuilder sb = new StringBuilder(prefix);
                    boolean and = false;
                    for (E e : iterable) {        
                        if (and) {
                            sb.append(delimiter);
                        }
                        sb.append(e);
                        and = true;
                    }
                    sb.append(suffix);
                    

                    【讨论】:

                      【解决方案17】:

                      在 .NET 中,如果可能,我会使用 String.join 方法,它允许您指定分隔符和字符串数组。可以使用 ToArray 将列表转换为数组,但我不知道这样做会对性能造成什么影响。

                      您提到的三种算法是我会使用的(我喜欢第二种,因为它没有 if 语句,但如果不知道长度,我会使用第三种,因为它不会重复代码) .第二个仅在列表不为空时才有效,因此可能需要另一个 if 语句。

                      第四个变体可能是在每个连接的元素前面放置一个分隔符,然后从结果中删除第一个分隔符。

                      如果您确实在循环中连接字符串,请注意对于非平凡的情况,使用 stringbuilder 将大大优于重复的字符串连接。

                      【讨论】:

                        【解决方案18】:

                        您可以编写自己的 AppendTostring(string, delimiter) 方法,当且仅当字符串不为空时附加分隔符。然后,您只需在任何循环中调用该方法,而不必担心何时追加以及何时不追加。

                        编辑:如果可用,当然最好在方法中使用某种 StringBuffer。

                        【讨论】:

                          【解决方案19】:
                          string result = "";
                          foreach(string item in list)
                          {
                              result += delimiter + item;
                          }
                          result = result.Substring(1);
                          

                          编辑:当然,您不会使用此算法或任何一种算法来连接字符串。对于 C#/.NET,您可能会使用 StringBuilder:

                          StringBuilder sb = new StringBuilder();
                          foreach(string item in list)
                          {
                              sb.Append(delimiter);
                              sb.Append(item);
                          }
                          string result = sb.ToString(1, sb.Length-1);
                          

                          这个解决方案的一个变体:

                          StringBuilder sb = new StringBuilder(list[0]);
                          for (int i=1; i<list.Count; i++)
                          {
                              sb.Append(delimiter);
                              sb.Append(list[i]);
                          }
                          string result = sb.ToString();
                          

                          两种解决方案都不包括任何错误检查。

                          【讨论】:

                          • 这是非常低效的——至少在 C# 中——因为它每次都会复制“到目前为止的字符串”。
                          • 我知道这是低效的(我会使用 StringBuilder)。但这不是问题。
                          • 问题是最好的方式。 “效率极低”和“最好”通常不会同时出现。
                          • 为什么不编辑答案以使用 StringBuilder?这与语言无关的整个概念在 IMO 中被打破了,所以至少给出一个适合 C# 的答案:)
                          • 这仍然涉及一个毫无意义的副本——因此我采取了不同的方法:)
                          【解决方案20】:

                          来自http://dogsblog.softwarehouse.co.zw/post/2009/02/11/IEnumerable-to-Comma-Separated-List-(and-more).aspx

                          开发时我最讨厌的是制作一个逗号分隔的 id 列表,它非常简单,但代码总是很丑陋...。常见的解决方案是循环遍历并在每个项目后放置一个逗号,然后删除最后一个字符,或者有一个 if 语句来检查你是在列表的开头还是结尾。以下是您可以在任何 IEnumberable 上使用的解决方案,即列表、数组等。这也是我能想到的最有效的方法,因为它依赖于比编辑字符串或使用 if 更好的赋值。

                          public static class StringExtensions
                          {
                              public static string Splice<T>(IEnumerable<T> args, string delimiter)
                              {
                                  StringBuilder sb = new StringBuilder();
                                  string d = "";
                                  foreach (T t in args)
                                  {
                                      sb.Append(d);
                                      sb.Append(t.ToString());
                                      d = delimiter;
                                  }
                                  return sb.ToString();
                              }
                          }
                          

                          现在它可以与任何 IEnumerable 一起使用,例如。

                          StringExtensions.Splice(billingTransactions.Select(t => t.id), ",")

                          给我们 31,32,35

                          【讨论】:

                            【解决方案21】:

                            对于java,这个question 或这个question 给出了非常完整的答案。

                            即在 Apache Commons 中使用 StringUtils.join

                            String result = StringUtils.join(list, ", ");
                            

                            【讨论】:

                              【解决方案22】:

                              在 Clojure 中,您可以只使用 clojure.contrib.str-utils/str-join:

                              (str-join ", " list)
                              

                              但是对于实际的算法:

                              (reduce (fn [res cur] (str res ", " cur)) list)
                              

                              【讨论】:

                                【解决方案23】:

                                Groovy 还有一个 String Object.join(String) 方法。

                                【讨论】:

                                  【解决方案24】:

                                  Java(来自 Jon 的解决方案):

                                      StringBuilder sb = new StringBuilder();
                                      String delimiter = "";
                                      for (String item : items) {
                                          sb.append(delimiter).append(item);
                                          delimeter = ", ";
                                      }
                                      return sb.toString();
                                  

                                  【讨论】: