Jak dołączyć int [] do ciągu oddzielonego znakami w .NET?


101

Mam tablicę liczb całkowitych:

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

Jaki jest najłatwiejszy sposób przekształcenia ich w pojedynczy ciąg, w którym liczby są oddzielone znakiem (na przykład "2,3,6,7":)?

Jestem w C # i .NET 3.5.


3
TAK Skały! W niedzielę otrzymałem 3 doskonałe odpowiedzi w ciągu 10 minut!
— Riri

4
Ponieważ .NET 4.0istnieją metody, które pobierają tablicę obiektów i IEnumerable, więc możesz to zrobić string.join(",", number). Wiem, że pytanie określa .NET 3.5, więc nie udzieliłem odpowiedzi, ale pojawia się w wyszukiwaniach, które nie określają wersji, a wiedza, że ​​jest to możliwe w 4.0, może komuś pomóc.
— Jason Goemaat

Odpowiedzi:


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"

EDYCJA : od (przynajmniej) .NET 4.5,

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

jest równa:

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

EDYCJA :

Widzę, że kilka rozwiązań reklamuje użycie StringBuilder. Ktoś narzeka, że ​​metoda Join powinna przyjmować argument IEnumerable.

Zawiodę Cię :) String.Join wymaga tablicy z jednego powodu - wydajności. Metoda Join musi znać rozmiar danych, aby skutecznie przydzielić niezbędną ilość pamięci.

Oto fragment wewnętrznej implementacji metody 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]);
    }
}

Jestem zbyt leniwy, aby porównać wydajność sugerowanych metod. Ale coś mi mówi, że Join wygra :)


Jest to prawdopodobnie najlepszy zakład przy użyciu podstawowych metod rozszerzających .NET, ale naprawdę chciałem, aby string.Join () zaakceptował IEnumerable <string>, aby uniknąć konwersji ToArray ().
— spoulson

Nic nie stoi na przeszkodzie, aby ktoś przeciążał string.Join, aby również wziąć IEnumerable. ;)
— Robert P

1
Jest to prawdopodobnie również najłatwiejsze rozwiązanie, a nie tylko najszybsze.
— Dave Van den Eynde

9
NET 4 udostępnia Przeciążenie String.Join, które akceptuje IEnumerable jako parametr. msdn.microsoft.com/en-us/library/dd783876.aspx
— Ryan Kohn

using System.Linq;jest wymagane.
— Gayan Weerakutti,

32

Chociaż w OP określono .NET 3.5, osoby, które chcą to zrobić w .NET 2.0 z C # 2, mogą to zrobić:

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

Uważam, że istnieje wiele innych przypadków, w których użycie funkcji Convert.xxx jest lepszą alternatywą dla lambdy, chociaż w języku C # 3 lambda może pomóc w wnioskowaniu o typie.

Dość kompaktowa wersja C # 3, która współpracuje z .NET 2.0 to:

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

11

Jedną z mieszanin tych dwóch podejść byłoby napisanie metody rozszerzenia w IEnumerable <T>, która używała StringBuilder. Oto przykład z różnymi przeciążeniami w zależności od tego, czy chcesz określić transformację, czy po prostu polegać na zwykłym ToString. Nazwałem metodę „JoinStrings” zamiast „Join”, aby uniknąć pomyłki z innym typem Join. Może ktoś wymyśli lepszą nazwę :)

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"), ","));
    }
}

Niezłe rozwiązanie! Nie potrzebujesz jednak parametru projekcji, możesz po prostu napisać x.Select (i => i.ToString ("X")). JoinStrings (";"), co jest bardziej idiomatyczne.
— JacquesB

Tak, myślałem o tym później. Czasami miło jest móc określić to wszystko za jednym zamachem, ale zdecydowanie bardziej elegancko jest podzielić operacje :)
— Jon Skeet

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

Musimy przekonwertować każdy z elementów na a, Stringzanim będziemy mogli je połączyć, więc sensowne jest użycie Selecti wyrażenie lambda. Jest to równoważne mapw niektórych innych językach. Następnie musimy przekonwertować wynikowy zbiór ciągów z powrotem na tablicę, ponieważString.Join akceptuje tylko tablicę ciągów.

ToArray()Jest nieco brzydki myślę. String.Joinpowinien naprawdę zaakceptować IEnumerable<String>, nie ma powodu, aby ograniczać go tylko do tablic. Dzieje się tak prawdopodobnie dlatego, że Joinpochodzi z czasów przed typami generycznymi, kiedy tablice były jedynym dostępnym rodzajem kolekcji.


5

Jeśli twoja tablica liczb całkowitych może być duża, uzyskasz lepszą wydajność, używając StringBuilder. Na przykład:

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

Edycja: Kiedy to opublikowałem, miałem błędne wrażenie, że „StringBuilder.Append (int value)” wewnętrznie zdołał dołączyć ciąg znaków reprezentujący wartość całkowitą bez tworzenia obiektu ciągu. To jest złe: sprawdzanie metody za pomocą Reflectora pokazuje, że po prostu dołącza value.ToString ().

Dlatego jedyną potencjalną różnicą w wydajności jest to, że ta technika pozwala uniknąć tworzenia jednej tablicy i nieco wcześniej zwalnia ciągi do wyrzucania elementów bezużytecznych. W praktyce nie da to żadnej wymiernej różnicy, więc głosowałem za tym lepszym rozwiązaniem .


Czy zmierzyłeś, że jest szybszy? String.Join używa również StringBuilder.
— JacquesB

Tak, ale najpierw musisz przekonwertować całość na tablicę, która jest daleka od ideału. W szczególności oznacza to, że musisz mieć wszystkie przekonwertowane ciągi w pamięci w tym samym czasie, zanim zaczniesz budować wynikowy ciąg.
— Jon Skeet

OTOH String.Join wstępnie oblicza rozmiar buforu StringBuilder, aby uniknąć zmiany rozmiaru. Więc może być szybszy, nawet jeśli wymaga więcej pamięci.
— JacquesB

5

Pytanie dotyczy „najłatwiejszego sposobu przekształcenia ich w pojedynczy ciąg, w którym liczby są oddzielone znakiem”.

Najłatwiej jest:

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

EDYCJA: To działa tylko w .NET 4.0+, brakowało mi wymagania .NET 3.5, gdy po raz pierwszy przeczytałem pytanie.


Nie jest to poprawne, ponieważ metoda string.Join przyjmuje tylko tablicę ciągów. Zajrzyj tutaj msdn.microsoft.com/en-us/library/tk0xe5h0.aspx
— ppolyzos

1
Jest to metoda przeciążona: msdn.microsoft.com/en-us/library/dd988350 Właśnie skopiowałem kod, który napisałem do nowej aplikacji konsolowej, dodałem Console.WriteLine i to jest wyjście: 2,3,6,7
— WebMasterP,

1
Myślę, że jest to dostępne tylko w .net 4
— Govind Malviya

Wymaga tablicy obiektów (lub tablicy ciągów), a nie tablicy int. Wystąpi błąd „połączenie jest niejednoznaczne”.
— LarryBud

2

Zgadzam się z wyrażeniem lambda dotyczącym czytelności i łatwości konserwacji, ale nie zawsze będzie to najlepsza opcja. Wadą stosowania zarówno podejścia IEnumerable / ToArray, jak i StringBuilder jest to, że muszą one dynamicznie rozwijać listę elementów lub znaków, ponieważ nie wiedzą, ile miejsca będzie potrzebne na końcowy ciąg.

W rzadkich przypadkach, w których szybkość jest ważniejsza niż zwięzłość, bardziej efektywne są poniższe czynności.

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

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

Pomyślałem też, że istnieje prostszy sposób. Nie wiesz o wydajności, ktoś ma jakiś (teoretyczny) pomysł?


To rozwiązanie dałoby ci „1,2,3,4,5”.
— Sarin

Dzięki, dodałem, Substring(1)żeby to naprawić (to było z pamięci).
— nieważne

2

W .NET 4.0 łączenie ciągów ma przeciążenie dla params object[], więc jest tak proste, jak:

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

przykład

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

W .NET 2.0 jest to trochę trudniejsze, ponieważ nie ma takiego przeciążenia. Potrzebujesz więc własnej ogólnej metody:

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

W .NET 3.5 można używać metod rozszerzających:

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

}

Możesz więc użyć metody rozszerzenia JoinArray.

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

Możesz również użyć tej metody rozszerzenia w .NET 2.0, jeśli dodasz ExtensionAttribute do swojego kodu:

// 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 {}
}


0

zapomnij o .net 3.5 i użyj następnego kodu w .net core

var result = string.Join(",", ints);
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.