Como juntar int [] a uma string separada por caracteres no .NET?

101

Eu tenho uma matriz de inteiros:

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

Qual é a maneira mais fácil de convertê-los em uma única string onde os números são separados por um caractere (como "2,3,6,7":)?

Estou em C # e .NET 3.5.

Riri
fonte
3
TÃO rochas! Recebi essas 3 respostas excelentes em 10 minutos em um domingo!
Riri
4
A partir de .NET 4.0então, existem métodos que usam uma matriz de objetos e um IEnumerable para que você possa apenas fazer string.join(",", number). Eu sei que a pergunta especifica .NET 3.5, então não respondi, mas ela aparece em pesquisas que não especificam uma versão e saber que é possível no 4.0 pode ajudar alguém.
Jason Goemaat

Respostas:

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"

EDITAR : a partir de (pelo menos) .NET 4.5,

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

é equivalente a:

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

EDITAR :

Vejo que várias soluções anunciam o uso de StringBuilder. Alguém reclama que o método Join deve ter um argumento IEnumerable.

Vou desapontá-lo :) String.Join requer array por um único motivo - desempenho. O método de junção precisa saber o tamanho dos dados para pré-alocar efetivamente a quantidade necessária de memória.

Aqui está uma parte da implementação interna do método 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]);
    }
}

Estou com preguiça de comparar o desempenho dos métodos sugeridos. Mas algo me diz que o Join vai ganhar :)

aku
fonte
Esta é provavelmente a melhor aposta usando métodos de extensão .NET básicos, mas eu realmente gostaria que string.Join () aceitasse um IEnumerable <string> para evitar a conversão de ToArray ().
spoulson
Nada impede que alguém sobrecarregue string.Join para pegar um IEnumerable também. ;)
Robert P de
1
Esta é provavelmente a solução mais fácil, e não apenas a mais rápida.
Dave Van den Eynde,
9
.NET 4 fornece uma sobrecarga String.Join que aceita IEnumerable como um parâmetro. msdn.microsoft.com/en-us/library/dd783876.aspx
Ryan Kohn
using System.Linq;É necessário.
Gayan Weerakutti
32

Embora o OP tenha especificado .NET 3.5, as pessoas que desejam fazer isso no .NET 2.0 com C # 2 podem fazer isso:

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

Acho que há vários outros casos em que o uso das funções Convert.xxx é uma alternativa mais adequada a um lambda, embora em C # 3 o lambda possa ajudar na inferência de tipo.

Uma versão C # 3 bastante compacta que funciona com .NET 2.0 é esta:

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

Uma mistura das duas abordagens seria escrever um método de extensão em IEnumerable <T> que usasse um StringBuilder. Aqui está um exemplo, com diferentes sobrecargas dependendo se você deseja especificar a transformação ou apenas contar com ToString simples. Nomeei o método "JoinStrings" em vez de "Join" para evitar confusão com o outro tipo de Join. Talvez alguém possa inventar um nome melhor :)

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
fonte
Ótima solução! No entanto, você não precisa do parâmetro de projeção, pode apenas escrever x.Select (i => i.ToString ("X")). JoinStrings (";") que é mais idiomático.
JacquesB
Sim, pensei nisso depois. Ocasionalmente, é bom poder especificar tudo de uma vez, mas é definitivamente mais elegante dividir as operações :)
Jon Skeet
8
String.Join(";", number.Select(item => item.ToString()).ToArray());

Temos que converter cada um dos itens em a Stringantes de podermos juntá-los, então faz sentido usar Selecte uma expressão lambda. Isso é equivalente a mapem alguns outros idiomas. Então, temos que converter a coleção de strings resultante em um array, porque String.Joinsó aceita um array de strings.

O ToArray()é um pouco feio eu acho. String.Joindeve realmente aceitar IEnumerable<String>, não há razão para restringi-lo apenas a arrays. Isso provavelmente ocorre porque Joiné anterior aos genéricos, quando os arrays eram o único tipo de coleção tipada disponível.

JacquesB
fonte
5

Se sua matriz de inteiros for grande, você obterá melhor desempenho usando um StringBuilder. Por exemplo:

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

Edit: Quando postei isso, tive a impressão equivocada de que "StringBuilder.Append (valor int)" conseguiu anexar internamente a representação de string do valor inteiro sem criar um objeto de string. Isso está errado: inspecionar o método com o Reflector mostra que ele simplesmente anexa value.ToString ().

Portanto, a única diferença de desempenho potencial é que essa técnica evita a criação de uma matriz e libera as strings para coleta de lixo um pouco mais cedo. Na prática, isso não fará nenhuma diferença mensurável, então votei a favor dessa solução melhor .

Joe
fonte
Você mediu para ser mais rápido? String.Join usa um StringBuilder também.
JacquesB
Sim, mas primeiro você precisa converter tudo em um array, o que está longe de ser o ideal. Em particular, significa que você precisa ter todas as strings convertidas na memória ao mesmo tempo, antes de começar a construir a string resultante.
Jon Skeet
OTOH String.Join calcula previamente o tamanho do buffer StringBuilder para evitar o redimensionamento. Portanto, pode ser mais rápido mesmo que exija mais memória.
JacquesB
5

A questão é a "maneira mais fácil de convertê-los em uma única string onde os números são separados por um caractere".

A maneira mais fácil é:

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

EDIT: Isso só funciona no .NET 4.0+, perdi o requisito do .NET 3.5 na primeira vez que li a pergunta.

WebMasterP
fonte
Isso não é válido como string. O método de junção leva apenas uma matriz de strings. Dê uma olhada aqui msdn.microsoft.com/en-us/library/tk0xe5h0.aspx
ppolyzos
1
É um método sobrecarregado: msdn.microsoft.com/en-us/library/dd988350 Acabei de copiar o código que escrevi em um novo aplicativo de console, adicionei um Console.WriteLine e este é o resultado: 2,3,6,7
WebMasterP
1
Acho que está disponível apenas em .net 4
Govind Malviya
Requer uma matriz de objeto (ou matriz de string), não uma matriz int. Irá dar um erro "a chamada é ambígua".
LarryBud
2

Concordo com a expressão lambda para legibilidade e manutenção, mas nem sempre será a melhor opção. A desvantagem de usar as abordagens IEnumerable / ToArray e StringBuilder é que eles precisam aumentar dinamicamente uma lista, seja de itens ou caracteres, pois não sabem quanto espaço será necessário para a string final.

Se o caso raro em que a velocidade é mais importante do que a concisão, o seguinte é mais eficiente.

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

Também achei que havia uma maneira mais simples. Não sabe sobre performance, alguém tem alguma ideia (teórica)?

vazio
fonte
Esta solução daria a você ", 1,2,3,4,5".
Sarin
Obrigado, adicionei Substring(1)para consertar isso (era de memória).
vazio em
2

No .NET 4.0, string join tem uma sobrecarga para params object[], então é tão simples quanto:

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

exemplo

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

No .NET 2.0, é um pouco mais difícil, já que não existe essa sobrecarga. Portanto, você precisa de seu próprio método genérico:

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

No .NET 3.5, você pode usar métodos de extensão:

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

}

Portanto, você pode usar o método de extensão JoinArray.

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

Você também pode usar esse método de extensão no .NET 2.0, se adicionar o ExtensionAttribute ao seu código:

// 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
fonte