如何在.NET中将int[]连接成一个用字符分隔的字符串?

110

我有一个整数数组:

int[] number = new int[] { 2,3,6,7 };

什么是最简单的方法将这些数字转换为单个字符串,其中数字由字符(如:"2,3,6,7")分隔?
我正在使用C#和.NET 3.5。

4
SO很棒!我在一个星期天的10分钟内就得到了这3个优秀的答案! - Riri
4
从“.NET 4.0”开始,有一些方法接受对象数组和IEnumerable参数,所以您可以只需执行string.join(",", number)。我知道该问题特定于.NET 3.5,所以我没有将其作为答案,但在未指定版本的搜索中会出现这种情况,知道在4.0中是可能的,可能会对某些人有所帮助。 - Jason Goemaat
11个回答

173
var ints = new int[] {1, 2, 3, 4, 5};
var result = string.Join(",", ints.Select(x => x.ToString()).ToArray());
Console.WriteLine(result); // prints "1,2,3,4,5"

至少从.NET 4.5开始,

var result = string.Join(",", ints.Select(x => x.ToString()).ToArray());

等价于:

var result = string.Join(",", ints);

我看到有几个解决方案宣传使用StringBuilder。有人抱怨Join方法应该接受IEnumerable参数。

我要让你失望了 :) String.Join之所以需要数组,只有一个原因 - 性能。 Join方法需要知道数据的大小,以有效地预分配必要的内存量。

这是String.Join方法内部实现的一部分:

// length computed from length of items in input array and length of separator
string str = FastAllocateString(length);
fixed (char* chRef = &str.m_firstChar) // note than we use direct memory access here
{
    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]);
    }
}

这可能是使用核心.NET扩展方法的最佳选择,但我真的希望string.Join()能够接受IEnumerable<string>,以避免ToArray()转换。 - spoulson
没有什么可以阻止某人重载string.Join以接受IEnumerable。 ;) - Robert P
1
这可能也是最简单的解决方案,不仅仅是最快的。 - Dave Van den Eynde
9
.NET 4 提供了一个 String.Join 的重载方法,它接受一个 IEnumerable 类型的参数。http://msdn.microsoft.com/zh-cn/library/dd783876.aspx - Ryan Kohn
需要使用 System.Linq; - Gayan Weerakutti

33

虽然OP指定了.NET 3.5,但想在.NET 2.0和C# 2.0中完成此操作的人也可以这样做:

string.Join(",", Array.ConvertAll<int, String>(ints, Convert.ToString));

我发现在其他很多情况下,使用Convert.xxx函数比使用lambda表达式更加简洁。尽管在C#3.0中,lambda表达式可以帮助类型推断。

以下是一个相当紧凑的C#3.0版本,适用于.NET 2.0:

string.Join(",", Array.ConvertAll(ints, item => item.ToString()))

11

将两种方法混合使用的一种方法是在IEnumerable<T>上编写一个扩展方法,该方法使用StringBuilder。以下是一个示例,具有不同的重载,取决于您是想指定转换还是仅依赖于普通的ToString。我将该方法命名为“JoinStrings”,而不是“Join”,以避免与其他类型的Join混淆。也许有人可以想出更好的名称:)

using System;
using System.Collections.Generic;
using System.Text;

public static class Extensions
{
    public static string JoinStrings<T>(this IEnumerable<T> source, 
                                        Func<T, string> projection, string separator)
    {
        StringBuilder builder = new StringBuilder();
        bool first = true;
        foreach (T element in source)
        {
            if (first)
            {
                first = false;
            }
            else
            {
                builder.Append(separator);
            }
            builder.Append(projection(element));
        }
        return builder.ToString();
    }

    public static string JoinStrings<T>(this IEnumerable<T> source, string separator)
    {
        return JoinStrings(source, t => t.ToString(), separator);
    }
}

class Test
{

    public static void Main()
    {
        int[] x = {1, 2, 3, 4, 5, 10, 11};

        Console.WriteLine(x.JoinStrings(";"));
        Console.WriteLine(x.JoinStrings(i => i.ToString("X"), ","));
    }
}

不错的解决方案!虽然您不需要projection参数,但是您可以使用更符合惯用法的方法x.Select(i => i.ToString("X")).JoinStrings(";")。 - JacquesB
是的,我之后想到了这一点。偶尔一次性指定所有操作可能很好,但将其分开执行肯定更优雅 :) - Jon Skeet

8
String.Join(";", number.Select(item => item.ToString()).ToArray());

我们需要将每个项目转换为String,然后才能将它们连接起来,因此使用Select和lambda表达式是有意义的。在其他一些语言中,这相当于map。然后,我们必须将生成的字符串集合转换回数组,因为String.Join仅接受字符串数组。
我认为ToArray()略显丑陋。实际上,String.Join应该接受IEnumerable<String>,没有理由限制它只能接受数组。这可能只是因为Join是在泛型之前出现的,当时数组是唯一可用的类型化集合。

5
如果您的整数数组可能很大,使用 StringBuilder 可以获得更好的性能。例如:
StringBuilder builder = new StringBuilder();
char separator = ',';
foreach(int value in integerArray)
{
    if (builder.Length > 0) builder.Append(separator);
    builder.Append(value);
}
string result = builder.ToString();

编辑:当我发布这篇文章时,我错误地认为“StringBuilder.Append(int value)”在内部成功附加了整数值的字符串表示形式而不创建字符串对象。这是错误的:使用Reflector检查该方法显示它只是附加了value.ToString()。
因此,唯一可能的性能差异是该技术避免了一个数组创建,并且稍微更快地释放了字符串进行垃圾回收。实际上,这不会产生任何可测量的差异,因此我已经支持this better solution

你测过它的速度吗?String.Join也使用了StringBuilder。 - JacquesB
是的,但你首先需要将整个东西转换为数组,这远非理想。特别是,这意味着在构建结果字符串之前,您需要同时将所有转换后的字符串保存在内存中。 - Jon Skeet
另一方面,String.Join 预先计算 StringBuilder 缓冲区的大小,因此它避免了重新调整大小。因此,即使需要更多的内存,它可能会更快。 - JacquesB

5

这个问题是关于“最简单的将数字转换为单个字符串,并在数字之间用字符分隔的方法”。

最简单的方法是:

int[] numbers = new int[] { 2,3,6,7 };
string number_string = string.Join(",", numbers);
// do whatever you want with your exciting new number string

这仅适用于.NET 4.0及以上版本。


这不是有效的,因为string.Join方法只接受字符串数组。请在此处查看http://msdn.microsoft.com/en-us/library/tk0xe5h0.aspx - ppolyzos
1
这是一个重载方法:http://msdn.microsoft.com/en-us/library/dd988350 我只是把我写的代码复制到一个新的控制台应用程序中,添加了一个Console.WriteLine,输出如下: 2,3,6,7 - WebMasterP
1
我认为这只在 .net 4 中可用。 - Govind Malviya
需要一个对象数组(或字符串数组),而不是整数数组。这将导致“调用不明确”的错误。 - LarryBud

3
ints.Aggregate("", ( str, n ) => str +","+ n ).Substring(1);

我也认为有一种更简单的方法。

这个解决方案会给你",1,2,3,4,5"。 - Sarin
谢谢,我已经添加了Substring(1)来修复它(这是从记忆中得出的)。 - void

3
在 .NET 4.0 中,字符串拼接具有一个 params object[] 重载,因此它可以变得简单:
int[] ids = new int[] { 1, 2, 3 };
string.Join(",", ids);

例子

int[] ids = new int[] { 1, 2, 3 };
System.Data.Common.DbCommand cmd = new System.Data.SqlClient.SqlCommand("SELECT * FROM some_table WHERE id_column IN (@bla)");
cmd.CommandText = cmd.CommandText.Replace("@bla",  string.Join(",", ids));

在.NET 2.0中,由于没有这样的重载,所以稍微有点难度。因此,您需要自己编写通用方法:
public static string JoinArray<T>(string separator, T[] inputTypeArray)
{
    string strRetValue = null;
    System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();

    for (int i = 0; i < inputTypeArray.Length; ++i)
    {
        string str = System.Convert.ToString(inputTypeArray[i], System.Globalization.CultureInfo.InvariantCulture);

        if (!string.IsNullOrEmpty(str))
        {
            // SQL-Escape
            // if (typeof(T) == typeof(string))
            //    str = str.Replace("'", "''");

            ls.Add(str);
        } // End if (!string.IsNullOrEmpty(str))

    } // Next i

    strRetValue= string.Join(separator, ls.ToArray());
    ls.Clear();
    ls = null;

    return strRetValue;
}

在.NET 3.5中,您可以使用扩展方法:
public static class ArrayEx
{

    public static string JoinArray<T>(this T[] inputTypeArray, string separator)
    {
        string strRetValue = null;
        System.Collections.Generic.List<string> ls = new System.Collections.Generic.List<string>();

        for (int i = 0; i < inputTypeArray.Length; ++i)
        {
            string str = System.Convert.ToString(inputTypeArray[i], System.Globalization.CultureInfo.InvariantCulture);

            if (!string.IsNullOrEmpty(str))
            {
                // SQL-Escape
                // if (typeof(T) == typeof(string))
                //    str = str.Replace("'", "''");

                ls.Add(str);
            } // End if (!string.IsNullOrEmpty(str))

        } // Next i

        strRetValue= string.Join(separator, ls.ToArray());
        ls.Clear();
        ls = null;

        return strRetValue;
    }

}

因此,您可以使用JoinArray扩展方法。

int[] ids = new int[] { 1, 2, 3 };
string strIdList = ids.JoinArray(",");

如果您在代码中添加了ExtensionAttribute,您也可以在.NET 2.0中使用该扩展方法:

// you need this once (only), and it must be in this namespace
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class ExtensionAttribute : Attribute {}
}

2

我认同使用lambda表达式可以提高代码的可读性和可维护性,但并非总是最佳选择。IEnumerable/ToArray和StringBuilder方法的缺点在于它们必须动态增长一个列表,无论是项还是字符,因为它们不知道最终字符串需要多少空间。

如果极少数情况下速度比简洁更重要,则以下方法更为高效。

int[] number = new int[] { 1, 2, 3, 4, 5 };
string[] strings = new string[number.Length];
for (int i = 0; i < number.Length; i++)
  strings[i] = number[i].ToString();
string result = string.Join(",", strings);

1

9
需要使用链接页面上定义的扩展方法,这可能需要更清晰明确的说明。 - StocksR

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