使用分隔符连接字符串

30

如何将字符串列表连接成一个以分隔符为分隔的字符串?主要关注何时停止添加分隔符,使用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循环的变化

来自Jon的解决方案。

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

使用迭代器

再次来自Jon

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。 - tehvan
如果您希望这是语言无关的,那么您不应该担心C#特定的优化(例如StringBuilder)。 - mpen
25个回答

43

由于不同的语言和平台对字符串的处理方式不同,并提供不同程度的内置支持,因此在这里很难给出一个真正与语言无关的答案。你可以用两种不同的语言编写几乎完全相同的代码,在一种语言中会很好,而在另一种语言中则会很差。

在C#中,你可以使用:

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

这将在除第一项外的所有项目前添加逗号。类似的代码在Java中也很好用。

编辑:这里有一个替代方法,类似于Ian后来的答案,但适用于通用的IEnumerable<string>

// 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<T> 参数类型,会自动调用 ToString 方法,还有一个重载接受 IEnumerable<string> 类型参数。因此,在.NET中你不再需要上面的代码。


1
更短但效率较低 - 在末尾截取子字符串仍然意味着不够优雅的复制。 - Jon Skeet
这是一个相当老的话题,但我觉得有必要指出一个不同的解决方案,如果你可以使用 StringBuilder 的话。该类有一个名为 Length 的属性,您可以随意更改它。因此,在循环中始终添加分隔符,并在循环结束后减少 Length 属性以匹配分隔符的长度。然而,性能差异是微不足道的。 - Steve
@ThomasB:我认为这不太可能成为性能问题:你要么想要去掉多余的逗号,要么不想。正确性更重要。 - Jon Skeet
使用字符串生成器的唯一不好的地方是你必须定义分隔逻辑,如果你最后只是使用数组作为后备存储器,你可以直接使用 string.Join(" ", myStringArray) - The Muffin Man
@TheMuffinMan:实际上,在更近的版本中,string.Join已经被重载了。我会添加的 :) - Jon Skeet
显示剩余3条评论

26
在.NET中,您可以使用String.Join方法
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方法,但泛型类型可以。 - Ray Lu
@CodeMelt:在.NET 3.5中,IEnumerable<T>可以使用Enumerable.ToArray扩展方法转换为数组。 - Jon Skeet
@Jon,我的第一条评论说通用的可以使用ToArray。IEnumerable是没有这个扩展方法的那一个。 - Ray Lu
谢谢,我看到这个线程中正在进行性能与便利性的讨论。 - Ray Lu
string.Join是个不错的方法,你能否看一下CodeMeIt的帖子并提供一些意见?先谢谢了。 - Ray Lu
显示剩余4条评论

8

在某些语言中只需要一行代码就可以实现这个功能,因此没有必要使它与语言无关。例如,Python提供了支持。

",".join(sequence)

请查看join文档以获取更多信息。


5

如果您使用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() 会使它们变成 'None',这不太好看 ;)

5
在 PHP 的 implode() 函数中:
$string = implode($delim, $array);

4
List<string> aaa = new List<string>{ "aaa", "bbb", "ccc" };
string mm = ";";
return aaa.Aggregate((a, b) => a + mm + b);

并且您可以获得以下内容
aaa;bbb;ccc

lambda非常方便


注意:由于反复复制结果字符串,这会导致Schlemiel Painter算法。最好使用重载的StringBuilder或string.Join。 - Pieter

4

如果需要,我会始终添加分隔符并在最后删除它。这样,当您只关心执行一次操作时,就不会在循环的每次迭代中执行 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)
}

4

我会递归地表达这个问题。

  • 检查字符串参数的数量是否为1。如果是,返回该字符串。
  • 否则递归,但将第一个和第二个参数与它们之间的分隔符组合。

Common Lisp示例:

(defun join (delimiter &rest strings)
  (if (null (rest strings))
      (first strings)
      (apply #'join
             delimiter
             (concatenate 'string
                          (first strings)
                          delimiter
                          (second strings))
             (cddr strings))))

更常见的方法是使用reduce,但这几乎扩展到与上述完全相同的说明:

(defun join (delimiter &rest strings)
  (reduce (lambda (a b)
            (concatenate 'string a delimiter b))
          strings))

3

IEnumerable类型的变量不能使用ToArray方法。 - Ray Lu

3
问题在于计算机语言很少有字符串布尔值,也就是说,类型为字符串的方法很少有实际用途。SQL Server至少有is[not]null和nullif这两个方法,它们结合使用可以解决分隔符问题: isnotnull(nullif(columnvalue, ""),"," + columnvalue))
问题在于,在语言中,有布尔类型和字符串类型,除了丑陋的编码形式外,二者永远不会相遇,如下所示:
concatstring = string1 + "," + string2; if (fubar) concatstring += string3 concatstring += string4 etc
我已经尽力避免所有这些丑陋的代码方式,玩逗号游戏并使用连接字符串的方法,但我仍然会遇到一些问题,包括当我忘记一个逗号并且一个变量为空时,出现的SQL Server错误。
乔纳森

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接