Wie verbinde ich int [] mit einer durch Zeichen getrennten Zeichenfolge in .NET?

101

Ich habe eine Reihe von ganzen Zahlen:

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

Was ist der einfachste Weg, diese in eine einzelne Zeichenfolge umzuwandeln, bei der die Zahlen durch ein Zeichen getrennt sind (wie :) "2,3,6,7"?

Ich bin in C # und .NET 3.5.

Riri
quelle
3
SO rockt! Ich habe diese 3 ausgezeichneten Antworten innerhalb von 10 Minuten an einem Sonntag erhalten!
Riri
4
Ab .NET 4.0sofort gibt es Methoden, die ein Array von Objekten und eine IEnumerable verwenden, sodass Sie dies einfach tun können string.join(",", number). Ich weiß, dass die Frage .NET 3.5 spezifiziert, daher habe ich dies nicht als Antwort angegeben, aber sie taucht bei Suchvorgängen auf, bei denen keine Version angegeben ist, und zu wissen, dass dies in 4.0 möglich ist, könnte jemandem helfen.
Jason Goemaat

Antworten:

162
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"

BEARBEITEN : Ab (mindestens) .NET 4.5,

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

ist äquivalent zu:

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

EDIT :

Ich sehe mehrere Lösungen, die die Verwendung von StringBuilder bewerben. Jemand beschwert sich, dass die Join-Methode ein IEnumerable-Argument annehmen sollte.

Ich werde dich enttäuschen :) String.Join erfordert Array aus einem einzigen Grund - Leistung. Die Join-Methode muss die Größe der Daten kennen, um die erforderliche Speichermenge effektiv vorzuweisen.

Hier ist ein Teil der internen Implementierung der String.Join-Methode:

// 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]);
    }
}

Ich bin zu faul, um die Leistung der vorgeschlagenen Methoden zu vergleichen. Aber irgendetwas sagt mir, dass Join gewinnen wird :)

aku
quelle
Dies ist wahrscheinlich die beste Wahl für die Verwendung von .NET-Kernerweiterungsmethoden, aber ich wünschte wirklich, string.Join () würde eine IEnumerable <string> akzeptieren, um die ToArray () -Konvertierung zu vermeiden.
Spoulson
Nichts hindert jemanden daran, einen String zu überladen. Schließen Sie sich an, um auch eine IEnumerable zu verwenden. ;)
Robert P
1
Dies ist wahrscheinlich auch die einfachste und nicht nur die schnellste Lösung.
Dave Van den Eynde
9
.NET 4 bietet eine String.Join-Überladung, die IEnumerable als Parameter akzeptiert. msdn.microsoft.com/en-us/library/dd783876.aspx
Ryan Kohn
using System.Linq;Wird benötigt.
Gayan Weerakutti
32

Obwohl das OP .NET 3.5 angegeben hat, können Benutzer, die dies in .NET 2.0 mit C # 2 tun möchten, Folgendes tun:

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

Ich finde, es gibt eine Reihe anderer Fälle, in denen die Verwendung der Convert.xxx-Funktionen eine bessere Alternative zu einem Lambda darstellt, obwohl das Lambda in C # 3 die Typinferenzierung unterstützen könnte.

Eine ziemlich kompakte C # 3-Version, die mit .NET 2.0 funktioniert, ist folgende:

string.Join(",", Array.ConvertAll(ints, item => item.ToString()))
Will Dean
quelle
11

Eine Mischung der beiden Ansätze wäre das Schreiben einer Erweiterungsmethode auf IEnumerable <T>, die einen StringBuilder verwendet. Hier ist ein Beispiel mit unterschiedlichen Überladungen, je nachdem, ob Sie die Transformation angeben oder sich nur auf einfachen ToString verlassen möchten. Ich habe die Methode "JoinStrings" anstelle von "Join" genannt, um Verwechslungen mit dem anderen Join-Typ zu vermeiden. Vielleicht kann sich jemand einen besseren Namen einfallen lassen :)

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"), ","));
    }
}
Jon Skeet
quelle
Schöne Lösung! Sie benötigen den Projektionsparameter jedoch nicht, sondern können einfach x.Select (i => i.ToString ("X")) schreiben. JoinStrings (";") ist idiomatischer.
JacquesB
Ja, darüber habe ich später nachgedacht. Gelegentlich ist es schön, alles auf einmal spezifizieren zu können, aber es ist definitiv eleganter, die Operationen aufzuteilen :)
Jon Skeet
8
String.Join(";", number.Select(item => item.ToString()).ToArray());

Wir müssen jedes Element in ein konvertieren, Stringbevor wir es verbinden können. Daher ist es sinnvoll, Selecteinen Lambda-Ausdruck zu verwenden. Dies entspricht mapeinigen anderen Sprachen. Dann müssen wir die resultierende Sammlung von Zeichenfolgen zurück in ein Array konvertieren, da String.Joinnur ein Zeichenfolgenarray akzeptiert wird.

Das ToArray()ist etwas hässlich finde ich. String.Joinsollte wirklich akzeptieren IEnumerable<String>, gibt es keinen Grund, es nur auf Arrays zu beschränken. Dies liegt wahrscheinlich nur daran Join, dass es sich um frühere Generika handelt, als Arrays die einzige verfügbare typisierte Sammlung waren.

JacquesB
quelle
5

Wenn Ihr Array von Ganzzahlen möglicherweise groß ist, erzielen Sie mit einem StringBuilder eine bessere Leistung. Z.B:

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();

Bearbeiten: Als ich dies veröffentlichte, hatte ich den falschen Eindruck, dass "StringBuilder.Append (int value)" es intern geschafft hat, die Zeichenfolgendarstellung des ganzzahligen Werts anzuhängen, ohne ein Zeichenfolgenobjekt zu erstellen. Dies ist falsch: Die Überprüfung der Methode mit Reflector zeigt, dass einfach value.ToString () angehängt wird.

Daher besteht der einzige mögliche Leistungsunterschied darin, dass diese Technik die Erstellung eines Arrays vermeidet und die Zeichenfolgen für die Speicherbereinigung etwas früher freigibt. In der Praxis macht dies keinen messbaren Unterschied, daher habe ich diese bessere Lösung positiv bewertet .

Joe
quelle
Haben Sie es gemessen, um schneller zu sein? String.Join verwendet auch einen StringBuilder.
JacquesB
Ja, aber Sie müssen zuerst das Ganze in ein Array konvertieren, was alles andere als ideal ist. Dies bedeutet insbesondere, dass Sie alle konvertierten Zeichenfolgen gleichzeitig im Speicher haben müssen, bevor Sie mit der Erstellung der resultierenden Zeichenfolge beginnen.
Jon Skeet
OTOH String.Join berechnet die Größe des StringBuilder-Puffers vor, um eine Größenänderung zu vermeiden. Es kann also schneller sein, selbst wenn mehr Speicher benötigt wird.
JacquesB
5

Die Frage ist nach "der einfachsten Möglichkeit, diese in eine einzelne Zeichenfolge umzuwandeln, bei der die Zahlen durch ein Zeichen getrennt sind".

Der einfachste Weg ist:

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

BEARBEITEN: Dies funktioniert nur in .NET 4.0+. Beim ersten Lesen der Frage habe ich die .NET 3.5-Anforderung übersehen.

WebMasterP
quelle
Dies ist als Zeichenfolge nicht gültig. Die Join-Methode akzeptiert nur ein Array von Zeichenfolgen. Schauen Sie hier msdn.microsoft.com/en-us/library/tk0xe5h0.aspx
ppolyzos
1
Es ist eine überladene Methode: msdn.microsoft.com/en-us/library/dd988350 Ich habe gerade den Code, den ich geschrieben habe, in eine neue Konsolen-App kopiert, eine Console.WriteLine hinzugefügt und dies ist die Ausgabe: 2,3,6,7
WebMasterP
1
Ich denke, dass dies nur in .net 4
Govind Malviya
Erfordert ein Objektarray (oder Stringarray), kein int-Array. Gibt einen Fehler "Anruf ist mehrdeutig" aus.
LarryBud
2

Ich stimme dem Lambda-Ausdruck für Lesbarkeit und Wartbarkeit zu, aber es wird nicht immer die beste Option sein. Der Nachteil bei der Verwendung der Ansätze IEnumerable / ToArray und StringBuilder besteht darin, dass eine Liste mit Elementen oder Zeichen dynamisch erweitert werden muss, da sie nicht wissen, wie viel Speicherplatz für die endgültige Zeichenfolge benötigt wird.

Wenn der seltene Fall, in dem Geschwindigkeit wichtiger als Prägnanz ist, ist das Folgende effizienter.

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);
DocMax
quelle
2
ints.Aggregate("", ( str, n ) => str +","+ n ).Substring(1);

Ich dachte auch, es gäbe einen einfacheren Weg. Sie wissen nichts über Leistung, hat jemand eine (theoretische) Idee?

Leere
quelle
Diese Lösung würde Ihnen ", 1,2,3,4,5" geben.
Sarin
Danke, ich habe hinzugefügt Substring(1), um das zu beheben (es war aus dem Speicher).
nichtig
2

In .NET 4.0 hat der String-Join eine Überladung für params object[], daher ist dies so einfach wie:

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

Beispiel

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));

In .NET 2.0 ist es ein bisschen schwieriger, da es keine solche Überlastung gibt. Sie benötigen also Ihre eigene generische Methode:

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;
}

In .NET 3.5 können Sie Erweiterungsmethoden verwenden:

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;
    }

}

Sie können also die JoinArray-Erweiterungsmethode verwenden.

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

Sie können diese Erweiterungsmethode auch in .NET 2.0 verwenden, wenn Sie Ihrem Code das ExtensionAttribute hinzufügen:

// 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 {}
}
Stefan Steiger
quelle