整型数组转换为字符串

81
在C#中,我有一个仅包含数字的int数组,我希望将该数组转换为字符串。
数组示例:
int[] arr = {0,1,2,3,0,1};

我该如何将它转换为格式为"012301"的字符串?

13个回答

127

在.NET 3.5中使用:

 String.Join("", new List<int>(array).ConvertAll(i => i.ToString()).ToArray());

在.net 4.0及以上版本中使用:(请参见@Jan Remunda的答案)

 string result = string.Join("", array);

8
有很多不必要的代码 :) 尝试这样写:String.Join("", new int[] {0,1,2,3,0,1}); - Jan Remunda
它的实现处理空值并将所有内容转换为字符串:http://referencesource.microsoft.com/#mscorlib/system/string.cs#125 - Jan Remunda

42
你可以简单地使用 String.Join 函数,并将分隔符设置为 string.Empty,因为它在内部使用了 StringBuilder
string result = string.Join(string.Empty, new []{0,1,2,3,0,1});
例如:如果您使用分号作为分隔符,结果将是0;1;2;3;0;1
实际上,使用null作为分隔符也可以正常工作,第二个参数可以是任何对象的可枚举集合,例如:
string result = string.Join(null, new object[]{0,1,2,3,0,"A",DateTime.Now});

8
此解决方案适用于.NET 4.0及更高版本。 - Jan Remunda

27
我意识到我的观点可能不是很流行,但我很难跳上Linq的列车。它很漂亮,很简洁。我明白这一点,也不反对在适当的情况下使用它。也许只有我觉得,人们已经停止思考创建实用函数来完成他们想要的事情,而更喜欢用(有时过长的)Linq代码来填充他们的代码,只为了创造一个密集的一行代码。
我并不是说人们提供的任何Linq答案都不好,但我觉得这些单行代码的潜力可能会越来越大,变得更加晦涩难懂,因为你需要处理各种情况。如果你的数组为空怎么办?如果你想要一个分隔符字符串而不仅仅是纯粹的连接?如果你的数组中有一些双位数的整数,并且你想要在每个值之前填充前导零,使每个元素的字符串与其余部分的长度相同?
以其中一个提供的答案为例:
        result = arr.Aggregate(string.Empty, (s, i) => s + i.ToString());

如果我需要担心数组是否为空,现在变成了这样:
        result = (arr == null) ? null : arr.Aggregate(string.Empty, (s, i) => s + i.ToString());

如果我想要一个逗号分隔的字符串,现在就变成了这样:
        result = (arr == null) ? null : arr.Skip(1).Aggregate(arr[0].ToString(), (s, i) => s + "," + i.ToString());

这段代码还不算太糟糕,但我认为一眼看上去并不清楚它在做什么。

当然,你可以将这行代码放入自己的实用程序函数中,以便不必在应用逻辑中混杂着繁琐冗长的代码,特别是如果你在多个地方都使用此功能:

    public static string ToStringLinqy<T>(this T[] array, string delimiter)
    {
        // edit: let's replace this with a "better" version using a StringBuilder
        //return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(array[0].ToString(), (s, i) => s + "," + i.ToString());
        return (array == null) ? null : (array.Length == 0) ? string.Empty : array.Skip(1).Aggregate(new StringBuilder(array[0].ToString()), (s, i) => s.Append(delimiter).Append(i), s => s.ToString());
    }

但是如果您将其放入实用程序函数中,那么您真的需要将其压缩成一行吗?在这种情况下,为什么不添加几行以增加清晰度并利用StringBuilder,以便您不会执行重复的连接操作:

    public static string ToStringNonLinqy<T>(this T[] array, string delimiter)
    {
        if (array != null)
        {
            // edit: replaced my previous implementation to use StringBuilder
            if (array.Length > 0)
            {
                StringBuilder builder = new StringBuilder();

                builder.Append(array[0]);
                for (int i = 1; i < array.Length; i++)
                {
                    builder.Append(delimiter);
                    builder.Append(array[i]);
                }

                return builder.ToString()
            }
            else
            {
                return string.Empty;
            }
        }
        else
        {
            return null;
        }
    }

如果您非常关注性能,您甚至可以将其转换为混合函数,根据数组中有多少元素来决定是使用string.Join还是使用StringBuilder(这是一种微小的优化,在我看来并不值得做,可能会带来更多的负面影响而非好处,但我将其用作此问题的示例):

    public static string ToString<T>(this T[] array, string delimiter)
    {
        if (array != null)
        {
            // determine if the length of the array is greater than the performance threshold for using a stringbuilder
            // 10 is just an arbitrary threshold value I've chosen
            if (array.Length < 10)
            {
                // assumption is that for arrays of less than 10 elements
                // this code would be more efficient than a StringBuilder.
                // Note: this is a crazy/pointless micro-optimization.  Don't do this.
                string[] values = new string[array.Length];

                for (int i = 0; i < values.Length; i++)
                    values[i] = array[i].ToString();

                return string.Join(delimiter, values);
            }
            else
            {
                // for arrays of length 10 or longer, use a StringBuilder
                StringBuilder sb = new StringBuilder();

                sb.Append(array[0]);
                for (int i = 1; i < array.Length; i++)
                {
                    sb.Append(delimiter);
                    sb.Append(array[i]);
                }

                return sb.ToString();
            }
        }
        else
        {
            return null;
        }
    }

对于这个示例,性能影响可能不值得关注,但重点是,如果您处于情况之中,实际上需要关注操作的性能,无论这些操作是什么,那么使用实用函数处理它很可能比使用复杂的Linq表达式更容易和更可读。

那个实用函数看起来还有点笨重。现在让我们摒弃混合物,这样做:

    // convert an enumeration of one type into an enumeration of another type
    public static IEnumerable<TOut> Convert<TIn, TOut>(this IEnumerable<TIn> input, Func<TIn, TOut> conversion)
    {
        foreach (TIn value in input)
        {
            yield return conversion(value);
        }
    }

    // concatenate the strings in an enumeration separated by the specified delimiter
    public static string Delimit<T>(this IEnumerable<T> input, string delimiter)
    {
        IEnumerator<T> enumerator = input.GetEnumerator();

        if (enumerator.MoveNext())
        {
            StringBuilder builder = new StringBuilder();

            // start off with the first element
            builder.Append(enumerator.Current);

            // append the remaining elements separated by the delimiter
            while (enumerator.MoveNext())
            {
                builder.Append(delimiter);
                builder.Append(enumerator.Current);
            }

            return builder.ToString();
        }
        else
        {
            return string.Empty;
        }
    }

    // concatenate all elements
    public static string ToString<T>(this IEnumerable<T> input)
    {
        return ToString(input, string.Empty);
    }

    // concatenate all elements separated by a delimiter
    public static string ToString<T>(this IEnumerable<T> input, string delimiter)
    {
        return input.Delimit(delimiter);
    }

    // concatenate all elements, each one left-padded to a minimum length
    public static string ToString<T>(this IEnumerable<T> input, int minLength, char paddingChar)
    {
        return input.Convert(i => i.ToString().PadLeft(minLength, paddingChar)).Delimit(string.Empty);
    }

现在我们有单独而相当紧凑的实用函数,每个函数都有自己的用处。
最终,我的观点不是你不应该使用Linq,而是说不要忘记创建自己的实用函数的好处,即使它们很小,可能只包含一行从Linq代码中返回结果的代码。如果没有其他问题,你将能够比使用Linq代码更加简洁地保持应用程序代码,并且如果你在多个地方使用它,则使用实用函数使得在需要稍后更改输出时更容易进行调整。
对于这个问题,我宁愿在我的应用程序代码中编写像这样的内容:
        int[] arr = { 0, 1, 2, 3, 0, 1 };

        // 012301
        result = arr.ToString<int>();

        // comma-separated values
        // 0,1,2,3,0,1
        result = arr.ToString(",");

        // left-padded to 2 digits
        // 000102030001
        result = arr.ToString(2, '0');

直到你创建了一个字符串数组并填充了它以便调用String.Join的那一刻,我才认为你是对的。这样做会浪费资源,而你可以利用延迟执行来完全跳过字符串数组。Join中的代码并不高深,编写一个通用的“Join”或“Delimit”方法(如果必须,则为扩展方法)来遍历枚举器并构建分隔符字符串是一个微不足道的练习。我理解你的观点并同意,但由于愚蠢的示例代码,我不能投赞成票。抱歉啊兄弟。 - Binary Worrier
你可以使用LINQ,而不必将所有内容都挤在一行中,从而获得延迟执行的强大功能。我同意,现在有太多挤在一起的LINQ示例了。这让我想起了很久以前学习C语言时,人们会写一行WTF代码来复制一个字符串"while(*dst++ = *src++);",并不是因为它更好/更快/更易读(绝对不是最后一个原因),而是因为他们能够这样做,这让他们(当然也包括我)感觉聪明。当然,无论你感觉聪明与否,代码都应该写得更清晰易懂。抱歉啰嗦了这么多。 - Binary Worrier
1
顺便说一下,我认为只有在使用返回结果集的函数(例如Intersect)时才能利用延迟执行,但如果您使用返回单个结果的函数(如Aggregate),则代码将立即执行所有元素。 在这种情况下,由于我们试图返回单个字符串值,我认为无法应用延迟执行。 - Dr. Wily's Apprentice
1
感谢评论,以下几点说明:1)如果性能是一个真正的问题,甚至没有讨论我的解决方案的必要,但在大多数工作中,性能可能并不是一个问题。我从常见情况开始。2)通常我会惯用地返回空集合而不是null,以减少混乱,正如您在早期示例中展示的原因。3)你对(缺乏)延迟执行是正确的。4)我认为这只是品味问题。对于从C++到C#的人来说,你最后的解决方案可能更清晰;我更喜欢更加体现简洁和功能性的方法。 - mqp
我完全同意,特别是检查 null 功能使 LINQ 变得臃肿和令人困惑。在编写单行代码时可能会让你感到兴奋,但在 12 个月后,下一个开发人员可能会想出一些他不能在广播中说出口的词。 - Russell
显示剩余2条评论

20
为了避免创建额外的数组,您可以执行以下操作。
var builder = new StringBuilder();
Array.ForEach(arr, x => builder.Append(x));
var res = builder.ToString();

尚未验证,但您可能可以传递 builder.Append 而不是 lambda 表达式,因为 builder.AppendArray.ForEach 中预期委托的签名匹配。 - Steve Guidi
3
史蒂夫,主意不错,但行不通,因为 ForEach() 期望的是 void,而 Append() 返回的是 StringBuilder - Danko Durbić

10
string result = arr.Aggregate("", (s, i) => s + i.ToString());

(免责声明:如果您有很多数字(至少数百个),并且在意性能,请避免使用此方法,而使用像JaredPar的答案中所述的StringBuilder。)


8
实际上这是非常隐蔽昂贵的。它具有“可伸缩”的内存需求,因此对于任何大于小型数组的内容都不理想。抱歉。 - Marc Gravell
1
这对于小数组来说还好,但是在处理更大的数组或者代码被频繁调用时,每次迭代都构建一个字符串的事实会引起担忧。 - Paul Ruane
这是绝对正确的,如果性能是一个问题,那么你不应该这样做。但是,如果只有少量数字,出于清晰和简洁的考虑,我会这样写(尽管我知道品味可能因人而异)。 - mqp
是的,Aggregate() 是正确的,但你应该使用 StringBuilder。请看我的回答。 - Danko Durbić

5
您可以做到:

 int[] arr = {0,1,2,3,0,1};
 string results = string.Join("",arr.Select(i => i.ToString()).ToArray());

这将为您提供结果。

5

我喜欢使用StringBuilderAggregate()。"诀窍"在于Append()返回StringBuilder实例本身:

var sb = arr.Aggregate( new StringBuilder(), ( s, i ) => s.Append( i ) );
var result = sb.ToString();     

1

这是一个绕远路的方式,但代码量不大,容易让初学者理解。

    int[] arr = {0,1,2,3,0,1};
    string joined = "";
    foreach(int i in arr){
        joined += i.ToString();
    }
    int number = int.Parse(joined);
    

1
我为了记录而留下了这个,但是不建议使用,因为它不是非常易读。尤其是现在,一段时间过后,当我回来看它时,想知道当我写下它时正在想什么(我可能在想“糟糕,必须在其他人发布答案之前把这个写完”)。
string s = string.Concat(arr.Cast<object>().ToArray());

+1. 如果您不需要任何分隔符,这是最简单的方法,这也是OP所要求的。当然,没有重载支持分隔符... - John Buchanan
(然而,我认为在这种情况下,它将被一个简单的StringBuilder解决方案严重超越。) - Paul Ruane

1
string.Join("", (from i in arr select i.ToString()).ToArray())

在.NET 4.0中,string.Join可以直接使用IEnumerable<string>
string.Join("", from i in arr select i.ToString())

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