Как присоединить int [] к строке, разделенной символами в .NET?

101

У меня есть массив целых чисел:

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

Как проще всего преобразовать их в одну строку, в которой числа разделены символом (например: "2,3,6,7" ?

Я использую C # и .NET 3.5.

Рири
источник
3
ТАК классно! Я получил эти 3 отличных ответа за 10 минут в воскресенье!
Riri
4
По .NET 4.0существуют методы , которые принимают массив объектов и IEnumerable , так что вы можете просто сделать string.join(",", number). Я знаю, что в вопросе указывается .NET 3.5, поэтому я не дал этого ответа, но он появляется при поиске, в котором не указывается версия, и знание того, что это возможно в 4.0, может кому-то помочь.
Джейсон Гоэмаат,

Ответы:

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"

РЕДАКТИРОВАТЬ : Начиная с (по крайней мере) .NET 4.5,

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

эквивалентно:

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

ИЗМЕНИТЬ :

Я вижу, что несколько решений рекламируют использование StringBuilder. Кто-то жалуется, что метод Join должен принимать аргумент IEnumerable.

Я вас разочарую :) String.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]);
    }
}

Мне лень сравнивать производительность предложенных методов. Но что-то мне подсказывает, что победит Join :)

аку
источник
Вероятно, это лучший вариант использования основных методов расширения .NET, но мне очень хотелось, чтобы string.Join () принимала IEnumerable <string>, чтобы избежать преобразования ToArray ().
spoulson
Ничто не мешает кому-то перегрузить строку. Присоединяйтесь, чтобы взять IEnumerable. ;)
Роберт П.
1
Это, вероятно, также самое простое решение, а не только самое быстрое.
Дэйв Ван ден Эйнде,
9
.NET 4 предоставляет перегрузку String.Join, которая принимает IEnumerable в качестве параметра. msdn.microsoft.com/en-us/library/dd783876.aspx
Райан Кон,
using System.Linq;необходимо.
Гаян Виракутти
32

Хотя OP указал .NET 3.5, люди, желающие сделать это в .NET 2.0 с C # 2, могут сделать это:

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

Я обнаружил, что есть ряд других случаев, когда использование функций Convert.xxx является более изящной альтернативой лямбда-выражению, хотя в C # 3 лямбда-функция может помочь в определении типа.

Вот довольно компактная версия C # 3, которая работает с .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"), ","));
    }
}
Джон Скит
источник
Хорошее решение! Однако вам не нужен параметр проекции, вы можете просто написать x.Select (i => i.ToString ("X")). JoinStrings (";"), что более идиоматично.
JacquesB
Да, я потом думал об этом. Иногда приятно иметь возможность указать все сразу, но определенно более элегантно разделить операции :)
Джон Скит
8
String.Join(";", number.Select(item => item.ToString()).ToArray());

Мы должны преобразовать каждый из элементов в a, Stringпрежде чем мы сможем присоединиться к ним, поэтому имеет смысл использовать Selectи лямбда-выражение. Это эквивалентно mapнекоторым другим языкам. Затем мы должны преобразовать полученную коллекцию строк обратно в массив, потому чтоString.Join принимает только массив строк.

ToArray()Немного некрасиво , я думаю. String.Joinдействительно должен принять IEnumerable<String>, нет причин ограничивать его только массивами. Вероятно, это просто потому, что Joinэто еще не все, когда массивы были единственным доступным типом коллекции.

JacquesB
источник
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 ().

Поэтому единственное возможное различие в производительности заключается в том, что этот метод позволяет избежать создания одного массива и несколько быстрее освобождает строки для сборки мусора. На практике это не будет иметь никакого измеримого значения, поэтому я проголосовал за это лучшее решение .

Джо
источник
Вы измерили, чтобы он был быстрее? String.Join также использует StringBuilder.
JacquesB
Да, но сначала нужно преобразовать все в массив, что далеко от идеала. В частности, это означает, что вам нужно иметь в памяти все преобразованные строки одновременно, прежде чем вы начнете строить результирующую строку.
Джон Скит,
OTOH 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

EDIT: это работает только в .NET 4.0+, я пропустил требование .NET 3.5, когда впервые прочитал вопрос.

WebMasterP
источник
Это недопустимо как строка. Метод Join принимает только массив строк. Посмотрите здесь msdn.microsoft.com/en-us/library/tk0xe5h0.aspx
ppolyzos 05
1
Это перегруженный метод: msdn.microsoft.com/en-us/library/dd988350 Я просто скопировал написанный мной код в новое консольное приложение, добавил Console.WriteLine и получил результат: 2,3,6,7
WebMasterP
1
Я думаю, что это доступно только в .net 4
Говинд Мальвия
Требуется массив объектов (или массив строк), а не массив int. Выдает ошибку "вызов неоднозначен".
LarryBud
2

Я согласен с лямбда-выражением для удобства чтения и поддержки, но это не всегда будет лучшим вариантом. Обратной стороной использования обоих подходов 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);
DocMax
источник
2
ints.Aggregate("", ( str, n ) => str +","+ n ).Substring(1);

Я тоже думал, что есть способ попроще. Не знаю о производительности, у кого-нибудь есть (теоретические) идеи?

пустота
источник
Это решение даст вам «1,2,3,4,5».
Зарин
Спасибо, я добавил, Substring(1)чтобы исправить это (это было по памяти).
недействительна
2

В .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(",");

Вы также можете использовать этот метод расширения в .NET 2.0, если вы добавите ExtensionAttribute в свой код:

// 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 {}
}
Стефан Штайгер
источник
1

Ты можешь сделать

ints.ToString(",")
ints.ToString("|")
ints.ToString(":")

Проверять, выписываться

Разделитель ToString с разделителями для массива, списка, словаря, универсального IEnumerable

Рэй Лу
источник
9
Для этого требуется метод расширения, определенный на связанной странице, это могло бы быть яснее
StocksR
0

забудьте о .net 3.5 и используйте следующий код в ядре .net

var result = string.Join(",", ints);
УкрГуру
источник