Làm cách nào để nối int [] với một chuỗi được phân tách bằng ký tự trong .NET?


101

Tôi có một mảng các số nguyên:

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

Cách dễ nhất để chuyển đổi chúng thành một chuỗi đơn trong đó các số được phân tách bằng ký tự (như "2,3,6,7":) là gì?

Tôi đang sử dụng C # và .NET 3.5.


3
Đá thật! Tôi đã nhận được 3 câu trả lời xuất sắc này trong vòng 10 phút vào Chủ nhật!
Riri 28/09/08

4
.NET 4.0có những phương thức lấy một mảng đối tượng và một IEnumerable để bạn có thể thực hiện string.join(",", number). Tôi biết câu hỏi chỉ định .NET 3.5 vì vậy tôi không đưa ra câu trả lời này, nhưng nó xuất hiện trong các tìm kiếm không chỉ định phiên bản và biết rằng nó có thể xảy ra trong 4.0 có thể giúp ai đó.
Jason Goemaat

Câu trả lời:


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"

CHỈNH SỬA : Kể từ (ít nhất) .NET 4.5,

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

tương đương với:

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

CHỈNH SỬA :

Tôi thấy một số giải pháp quảng cáo việc sử dụng StringBuilder. Một người nào đó phàn nàn rằng phương thức Tham gia nên lấy đối số IEnumerable.

Tôi sẽ làm bạn thất vọng :) String.Join yêu cầu mảng vì một lý do duy nhất - hiệu suất. Phương pháp tham gia cần biết kích thước của dữ liệu để phân bổ trước một cách hiệu quả lượng bộ nhớ cần thiết.

Đây là một phần của việc triển khai nội bộ phương thức 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]);
    }
}

Tôi quá lười biếng để so sánh hiệu suất của các phương pháp được đề xuất. Nhưng có điều gì đó nói với tôi rằng Tham gia sẽ thắng :)


Đây có lẽ là cách tốt nhất khi sử dụng các phương thức mở rộng .NET cốt lõi, nhưng tôi thực sự ước string.Join () sẽ chấp nhận <string> IEnumerable để tránh chuyển đổi ToArray ().
spoulson

Không có gì ngăn cản ai đó quá tải chuỗi. Hãy tham gia để lấy IEnumerable. ;)
Robert P

1
Đây có lẽ cũng là giải pháp dễ nhất và không chỉ là nhanh nhất.
Dave Van den Eynde 22/10/08

9
.NET 4 cung cấp quá tải String.Join chấp nhận IEnumerable làm tham số. msdn.microsoft.com/en-us/library/dd783876.aspx
Ryan Kohn

using System.Linq;bắt buộc.
Gayan Weerakutti

32

Mặc dù OP đã chỉ định .NET 3.5, những người muốn thực hiện việc này trong .NET 2.0 với C # 2 có thể thực hiện điều này:

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

Tôi thấy có một số trường hợp khác trong đó việc sử dụng các hàm Convert.xxx là một sự thay thế gọn gàng hơn cho lambda, mặc dù trong C # 3, lambda có thể giúp ích cho việc truyền kiểu.

Một phiên bản C # 3 khá nhỏ gọn hoạt động với .NET 2.0 là:

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

11

Một hỗn hợp của hai cách tiếp cận là viết một phương thức mở rộng trên IEnumerable <T> sử dụng một StringBuilder. Đây là một ví dụ, với các mức quá tải khác nhau tùy thuộc vào việc bạn muốn chỉ định chuyển đổi hay chỉ dựa vào ToString đơn giản. Tôi đã đặt tên phương thức là "JoinStrings" thay vì "Join" để tránh nhầm lẫn với loại Join khác. Có lẽ ai đó có thể nghĩ ra một cái tên hay hơn :)

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

Giải pháp tốt! Tuy nhiên, bạn không cần tham số chiếu, bạn có thể chỉ cần viết x.Select (i => i.ToString ("X")). JoinStrings (";") dễ hiểu hơn.
JacquesB 28-08

Vâng, tôi đã nghĩ về điều đó sau đó. Đôi khi nó là tốt đẹp để có thể xác định nó tất cả trong một đi, nhưng nó chắc chắn là tao nhã hơn để phân chia các hoạt động :)
Jon Skeet

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

Chúng ta phải chuyển đổi từng mục thành a Stringtrước khi có thể nối chúng, do đó, việc sử dụng Selectvà biểu thức lambda sẽ rất hợp lý. Điều này tương đương với maptrong một số ngôn ngữ khác. Sau đó, chúng ta phải chuyển đổi tập hợp kết quả của chuỗi trở lại một mảng, bởi vì String.Joinchỉ chấp nhận một mảng chuỗi.

Các ToArray()là hơi xấu xí tôi nghĩ. String.Jointhực sự nên chấp nhận IEnumerable<String>, không có lý do gì để hạn chế nó chỉ trong các mảng. Điều này có lẽ chỉ vì Joincó từ trước generic, khi mảng là loại tập hợp được đánh máy duy nhất có sẵn.


5

Nếu mảng số nguyên của bạn có thể lớn, bạn sẽ nhận được hiệu suất tốt hơn khi sử dụng StringBuilder. Ví dụ:

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

Chỉnh sửa: Khi tôi đăng bài này, tôi có ấn tượng nhầm rằng "StringBuilder.Append (int value)" được quản lý nội bộ để nối biểu diễn chuỗi của giá trị số nguyên mà không tạo đối tượng chuỗi. Điều này là sai: việc kiểm tra phương thức bằng Reflector cho thấy rằng nó chỉ cần thêm giá trị.ToString ().

Do đó, sự khác biệt về hiệu suất tiềm năng duy nhất là kỹ thuật này tránh tạo một mảng và giải phóng các chuỗi để thu gom rác sớm hơn một chút. Trong thực tế, điều này sẽ không tạo ra bất kỳ sự khác biệt nào có thể đo lường được, vì vậy tôi đã ủng hộ giải pháp tốt hơn này .


Bạn đã đo nó để nhanh hơn chưa? String.Join cũng sử dụng StringBuilder.
JacquesB 28/09/08

Có, nhưng trước tiên bạn cần chuyển đổi toàn bộ thành một mảng, điều này không lý tưởng lắm. Đặc biệt, nó có nghĩa là bạn cần có tất cả các chuỗi được chuyển đổi trong bộ nhớ cùng một lúc, trước khi bắt đầu xây dựng chuỗi kết quả.
Jon Skeet

OTOH String.Join tính toán trước kích thước của bộ đệm StringBuilder để tránh thay đổi kích thước. Vì vậy, nó có thể nhanh hơn ngay cả khi nó đòi hỏi nhiều bộ nhớ hơn.
JacquesB 28/09/08

5

Câu hỏi dành cho "cách dễ nhất để chuyển đổi chúng thành một chuỗi đơn trong đó số được phân tách bằng một ký tự".

Cách dễ nhất là:

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

CHỈNH SỬA: Điều này chỉ hoạt động trong .NET 4.0+, tôi đã bỏ lỡ yêu cầu .NET 3.5 trong lần đầu tiên tôi đọc câu hỏi.


Phương thức này không hợp lệ vì phương thức string.Join chỉ nhận một mảng các chuỗi. Hãy xem tại đây msdn.microsoft.com/en-us/library/tk0xe5h0.aspx
ppolyzos

1
Đó là một phương thức quá tải: msdn.microsoft.com/en-us/library/dd988350 Tôi vừa sao chép mã tôi đã viết vào một ứng dụng console mới, thêm Console.WriteLine và đây là kết quả: 2,3,6,7
WebMasterP

1
Tôi nghĩ rằng đây là chỉ có sẵn ở .net 4
Govind Malviya

Yêu cầu mảng đối tượng (hoặc mảng chuỗi), không phải mảng int. Sẽ đưa ra lỗi "cuộc gọi không rõ ràng".
LarryBud

2

Tôi đồng ý với biểu thức lambda về tính dễ đọc và khả năng bảo trì, nhưng nó sẽ không phải lúc nào cũng là lựa chọn tốt nhất. Nhược điểm của việc sử dụng cả hai phương pháp IEnumerable / ToArray và StringBuilder là chúng phải phát triển động một danh sách, một trong hai mục hoặc ký tự, vì chúng không biết sẽ cần bao nhiêu dung lượng cho chuỗi cuối cùng.

Nếu trường hợp hiếm hoi mà tốc độ quan trọng hơn tính ngắn gọn, thì cách sau sẽ hiệu quả hơn.

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

Tôi cũng nghĩ có một cách đơn giản hơn. Không biết về hiệu suất, bất cứ ai có bất kỳ ý tưởng (lý thuyết)?


Giải pháp này sẽ cung cấp cho bạn ", 1,2,3,4,5".
Sarin

Cảm ơn, tôi đã thêm Substring(1)để sửa lỗi đó (nó là từ bộ nhớ).
vô hiệu

2

Trong .NET 4.0, tham gia chuỗi có quá tải cho params object[], vì vậy nó đơn giản như:

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

thí dụ

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

Trong .NET 2.0, nó khó hơn một chút vì không có quá tải như vậy. Vì vậy, bạn cần phương pháp chung của riêng mình:

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

Trong .NET 3.5, bạn có thể sử dụng các phương thức mở rộng:

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

}

Vì vậy, bạn có thể sử dụng phương thức mở rộng JoinArray.

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

Bạn cũng có thể sử dụng phương thức tiện ích mở rộng đó trong .NET 2.0, nếu bạn thêm Thuộc tính mở rộng vào mã của mình:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.