IList <string> 또는 IEnumerable <string>에서 쉼표로 구분 된 목록 작성


848

IList<string>또는 에서 쉼표로 구분 된 문자열 값 목록을 만드는 가장 깨끗한 방법은 무엇입니까 IEnumerable<string>?

String.Join(...)에서 작동 string[]하므로 문자열 배열로 변환 IList<string>하거나 IEnumerable<string>쉽게 변환 할 수없는 경우 작업하기가 번거로울 수 있습니다 .


5
아 ... 3.5에서 ToArray 확장 메서드를 추가 한 것을 놓쳤다.public static TSource[] ToArray<TSource>(this IEnumerable<TSource> source)
Daniel Fortunov

1
CSV를 작성하는 방법을 찾는이 질문에 도달 한 경우 항목 사이에 쉼표를 삽입하는 것만으로는 충분하지 않으며 소스 데이터에 따옴표와 쉼표가있는 경우 실패 할 수 있음을 기억해야합니다.
쓰셨

답변:


1446

.NET 4 이상

IList<string> strings = new List<string>{"1","2","testing"};
string joined = string.Join(",", strings);

세부 및 사전 .Net 4.0 솔루션

IEnumerable<string>LINQ (.NET 3.5) 를 사용하면 문자열 배열로 매우 쉽게 변환 할 수 있습니다 .

IEnumerable<string> strings = ...;
string[] array = strings.ToArray();

필요한 경우 동등한 도우미 메소드를 작성하는 것이 쉽습니다.

public static T[] ToArray(IEnumerable<T> source)
{
    return new List<T>(source).ToArray();
}

그런 다음 다음과 같이 호출하십시오.

IEnumerable<string> strings = ...;
string[] array = Helpers.ToArray(strings);

그런 다음에 전화 할 수 있습니다 string.Join. 물론, 당신은하지 않습니다 도우미 메서드를 사용하여 :

// C# 3 and .NET 3.5 way:
string joined = string.Join(",", strings.ToArray());
// C# 2 and .NET 2.0 way:
string joined = string.Join(",", new List<string>(strings).ToArray());

후자는 약간 한 입입니다 :)

에 대한 다른 질문이 성능을 포함하여 (이에 국한되지 않음)처럼 정확히 - 이것은 아주 성능이 좋은뿐만 아니라 그것을 할 수있는 간단한 방법이 될 가능성이 있으며, 이 중 하나를 .

.NET 4.0부터에서 사용할 수있는 과부하가 더 string.Join많으므로 실제로 다음과 같이 작성할 수 있습니다.

string joined = string.Join(",", strings);

훨씬 간단합니다 :)


도우미 방법에는 두 개의 불필요한 목록을 만드는 것이 포함됩니다. 이것이 실제로 문제를 해결하는 가장 좋은 방법입니까? foreach 루프에서 직접 연결하지 않겠습니까?
Eric

4
도우미 메서드는 하나의 목록과 하나의 배열 만 만듭니다 . 요점은 결과는 목록이 아닌 배열이어야하며 시작하기 전에 배열의 크기를 알아야한다는 것입니다. 모범 사례에 따르면 LINQ에서 소스를 한 번 이상 열거하지 않아야합니다. 따라서 버퍼를 읽고 크기를 조정할 때 그대로 유지 List<T>됩니다. 이것이 바로 그 일 입니다. 왜 바퀴를 재발 명합니까?
Jon Skeet

9
요점은 결과가 연결된 문자열이어야한다는 것입니다. 이 목표를 달성하기 위해 새 목록이나 새 배열을 만들 필요가 없습니다. 이런 종류의 .NET 사고는 나를 슬프게 만듭니다.
Eric

38
그게 다야. 모든 대답은 Jon Skeet으로 연결됩니다. var PurchaseBooks = AmazonContainer.Where (p => p.Author == "Jon Skeet"). Select ();
Zachary Scott

3
@ codeMonkey0110 : 거기에 쿼리 표현식이 있거나 호출 할 필요가 없습니다 ToList. string myStr = string.Join(",", foo.Select(a => a.someInt.ToString()))그래도 사용하는 것이 좋습니다.
Jon Skeet

179

참고로, .NET 4.0 버전의 string.Join()일부 오버로드IEnumerable배열 을 대신하여 작동 합니다 T.

public static string Join(string separator, IEnumerable<string> values)
public static string Join<T>(string separator, IEnumerable<T> values)

2
이것은 T.ToString () 메소드를 호출합니까?
Philippe Lavoie

Jon의 대답에 대해 이것을 언급하려고했습니다. 언급 해 주셔서 감사합니다.
Dan Bechard 2009 년

2
어쨌든 객체의 속성 에서이 작업을 수행합니까? (예 : IEnumerable <Employee>이고 Employee 개체에는 문자열 .SSN 속성이 있고 쉼표로 구분 된 SSN 목록을 가져옵니다.)
granadaCoder

1
문자열을 먼저 선택해야하지만 그렇게하는 확장 메서드를 만들 수 있습니다. str = emps.Select(e => e.SSN).Join(",")
Xavier Poinas

65

이 작업을 수행하는 가장 쉬운 방법은 LINQ Aggregate방법을 사용하는 것입니다.

string commaSeparatedList = input.Aggregate((a, x) => a + ", " + x)

20
그것은 ToArray + Join보다 더 복잡한 (IMO) 일뿐 만 아니라 입력 시퀀스가 ​​크면 다소 비효율적입니다.
Jon Skeet

35
아직도, 그것은 가장 예쁘다.
메리트

2
StringBuilder 시드에 Aggregate를 공급하면 Aggregate Func가됩니다 Func<StringBuilder,string,StringBuider>. 그런 다음 ToString()반환 된 StringBuilder를 호출 하십시오. 물론 예쁘지는 않습니다 :)
Matt Greer

3
이것은 질문이 IMHO에 요청한 것을 수행하는 가장 명확한 방법입니다.
Derek Morrison

8
그주의 input.Count해야합니다 이상 1
Youngjae

31

쉼표로 구분 된 문자열 값 목록을 만드는 가장 깨끗한 방법은 다음과 같습니다.

string.Join<string>(",", stringEnumerable);

전체 예는 다음과 같습니다.

IEnumerable<string> stringEnumerable= new List<string>();
stringList.Add("Comma");
stringList.Add("Separated");

string.Join<string>(",", stringEnumerable);

도우미 기능을 만들 필요가 없으며 .NET 4.0 이상에 내장되어 있습니다.


4
이것은 Xavier가 그의 대답에서 지적했듯이 .NET 4부터 적용 가능합니다.
데릭 모리슨

한 달 미만의 경험을 가진 .NET 4 초보자의 관점에서이 답변은 정확성과 간결함의 훌륭한 조합이었습니다
Dexygen

13

실적을 기준으로 비교하면 "루프, sb. 추가 및 백스텝"입니다. 실제로 "다음 열거 및 수동 이동"도 동일합니다 (stddev 고려).

BenchmarkDotNet=v0.10.5, OS=Windows 10.0.14393
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233539 Hz, Resolution=309.2587 ns, Timer=TSC
  [Host] : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  Clr    : Clr 4.0.30319.42000, 64bit RyuJIT-v4.6.1637.0
  Core   : .NET Core 4.6.25009.03, 64bit RyuJIT


                Method |  Job | Runtime |     Mean |     Error |    StdDev |      Min |      Max |   Median | Rank |  Gen 0 | Allocated |
---------------------- |----- |-------- |---------:|----------:|----------:|---------:|---------:|---------:|-----:|-------:|----------:|
            StringJoin |  Clr |     Clr | 28.24 us | 0.4381 us | 0.3659 us | 27.68 us | 29.10 us | 28.21 us |    8 | 4.9969 |   16.3 kB |
 SeparatorSubstitution |  Clr |     Clr | 17.90 us | 0.2900 us | 0.2712 us | 17.55 us | 18.37 us | 17.80 us |    6 | 4.9296 |  16.27 kB |
     SeparatorStepBack |  Clr |     Clr | 16.81 us | 0.1289 us | 0.1206 us | 16.64 us | 17.05 us | 16.81 us |    2 | 4.9459 |  16.27 kB |
            Enumerable |  Clr |     Clr | 17.27 us | 0.0736 us | 0.0615 us | 17.17 us | 17.36 us | 17.29 us |    4 | 4.9377 |  16.27 kB |
            StringJoin | Core |    Core | 27.51 us | 0.5340 us | 0.4995 us | 26.80 us | 28.25 us | 27.51 us |    7 | 5.0296 |  16.26 kB |
 SeparatorSubstitution | Core |    Core | 17.37 us | 0.1664 us | 0.1557 us | 17.15 us | 17.68 us | 17.39 us |    5 | 4.9622 |  16.22 kB |
     SeparatorStepBack | Core |    Core | 15.65 us | 0.1545 us | 0.1290 us | 15.45 us | 15.82 us | 15.66 us |    1 | 4.9622 |  16.22 kB |
            Enumerable | Core |    Core | 17.00 us | 0.0905 us | 0.0654 us | 16.93 us | 17.12 us | 16.98 us |    3 | 4.9622 |  16.22 kB |

암호:

public class BenchmarkStringUnion
{
    List<string> testData = new List<string>();
    public BenchmarkStringUnion()
    {
        for(int i=0;i<1000;i++)
        {
            testData.Add(i.ToString());
        }
    }
    [Benchmark]
    public string StringJoin()
    {
        var text = string.Join<string>(",", testData);
        return text;
    }
    [Benchmark]
    public string SeparatorSubstitution()
    {
        var sb = new StringBuilder();
        var separator = String.Empty;
        foreach (var value in testData)
        {
            sb.Append(separator).Append(value);
            separator = ",";
        }
        return sb.ToString();
    }

    [Benchmark]
    public string SeparatorStepBack()
    {
        var sb = new StringBuilder();
        foreach (var item in testData)
            sb.Append(item).Append(',');
        if (sb.Length>=1) 
            sb.Length--;
        return sb.ToString();
    }

    [Benchmark]
    public string Enumerable()
    {
        var sb = new StringBuilder();
        var e = testData.GetEnumerator();
        bool  moveNext = e.MoveNext();
        while (moveNext)
        {
            sb.Append(e.Current);
            moveNext = e.MoveNext();
            if (moveNext) 
                sb.Append(",");
        }
        return sb.ToString();
    }
}

https://github.com/dotnet/BenchmarkDotNet 이 사용되었습니다


11

ToString ()이 아닌 객체 목록의 특정 속성을 조인하는 동안 여기에 도달했기 때문에 여기에 허용되는 답변이 추가되었습니다.

var commaDelimited = string.Join(",", students.Where(i => i.Category == studentCategory)
                                 .Select(i => i.FirstName));

9

다른 확장 방법은 다음과 같습니다.

    public static string Join(this IEnumerable<string> source, string separator)
    {
        return string.Join(separator, source);
    }

8

이 토론에 조금 늦게 도착했지만 이것이 저의 공헌입니다. I는 한 IList<Guid> OrderIds를 CSV 문자열로 변환하지만, 다음은 제네릭과 다른 유형 수정되지 않은 작동합니다 :

string csv = OrderIds.Aggregate(new StringBuilder(),
             (sb, v) => sb.Append(v).Append(","),
             sb => {if (0 < sb.Length) sb.Length--; return sb.ToString();});

짧고 달콤한, 새 문자열을 구성하기 위해 StringBuilder를 사용하고, 마지막 쉼표를 제거하기 위해 StringBuilder 길이를 1 씩 줄이며 CSV 문자열을 반환합니다.

Append()문자열 + 쉼표를 추가 하기 위해 여러 개를 사용하도록 이것을 업데이트했습니다 . James의 피드백에서 Reflector를 사용하여을 살펴 보았습니다 StringBuilder.AppendFormat(). 밝혀 AppendFormat()용도는 모두 StringBuilder는 여러 사용하는 것보다 이러한 맥락에서 덜 효율적 형식 문자열 구성하기 Appends()'들.


Xavier 덕분에 .Net4의 업데이트를 알지 못했습니다. 내가 작업중 인 프로젝트는 아직 도약하지 않았으므로 그 동안 지금 보행자 예제를 계속 사용할 것입니다.
David Clarke

2
항목이없는 IEnumerable 소스로는 실패합니다. sb. 길이-바운드 검사가 필요합니다.
James Dunne

James에게 감사의 말을 전합니다. 저는 이것을 사용하는 상황에서 적어도 하나의 OrderId를 갖도록 "보장되었습니다". 경계 확인을 포함하도록 예제와 내 코드를 모두 업데이트했습니다 (확실히).
David Clarke

@ 제임스 Sb.Length라고 부르는 것 같아요. 효과적으로 각 반복에서 수행하지 않고 끝까지 "if (notdone)"테스트를 피하고 있습니다.
David Clarke

1
@James 내 요점은 종종 여기에 묻는 질문에 대한 대답이 두 개 이상 있으며 "해킹"이라고 언급하면 ​​내가 맞지 않는 것이 잘못되었음을 의미합니다. 적은 수의 guids에 대해 위의 Daniel의 대답을 연결하는 것은 아마도 완벽하게 적합 할 것이며 아마도 내 대답보다 간결하고 읽기 쉽습니다. 내 코드에서 한 곳에서만 이것을 사용하고 있으며 쉼표를 구분 기호로만 사용합니다. YAGNI는 필요없는 것을 만들지 말라고 말합니다. DRY는 확장 방법을 만드는 시점에서 두 번 이상 수행해야하는 경우에 적용됩니다. HTH.
David Clarke

7

약간 모호한 것이지만 작동합니다.

string divisionsCSV = String.Join(",", ((List<IDivisionView>)divisions).ConvertAll<string>(d => d.DivisionID.ToString("b")).ToArray());

변환기에 변환기를 제공 한 후 목록에서 CSV를 제공합니다 (이 경우 d => d.DivisionID.ToString ( "b")).

해 키지 만 작동합니다-아마도 확장 방법으로 만들 수 있습니까?


7

다른 언어로 수행 한 방식을 사용하여 수행 한 방식은 다음과 같습니다.

private string ToStringList<T>(IEnumerable<T> list, string delimiter)
{
  var sb = new StringBuilder();
  string separator = String.Empty;
  foreach (T value in list)
  {
    sb.Append(separator).Append(value);
    separator = delimiter;
  }
  return sb.ToString();
}

7

예를 들어 '로 둘러 쌓아야 할 특정 요구 사항 :

        string[] arr = { "jj", "laa", "123" };
        List<string> myList = arr.ToList();

        // 'jj', 'laa', '123'
        Console.WriteLine(string.Join(", ",
            myList.ConvertAll(m =>
                string.Format("'{0}'", m)).ToArray()));

4

다음과 같은 유틸리티 함수가 있습니다 :

public static string Join<T>( string delimiter, 
    IEnumerable<T> collection, Func<T, string> convert )
{
    return string.Join( delimiter, 
        collection.Select( convert ).ToArray() );
}

많은 컬렉션을 쉽게 결합하는 데 사용할 수 있습니다.

int[] ids = {1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233};

string csv = StringUtility.Join(",", ids, i => i.ToString() );

intellisense가 컬렉션 유형을 선택하기 때문에 람다 앞에 컬렉션 매개 변수가 있습니다.

이미 문자열 열거가있는 경우 ToArray 만하면됩니다.

string csv = string.Join( ",", myStrings.ToArray() );

2
나는 거의 정확하게 똑같은 일을하는 확장 방법을 가지고있다 : stackoverflow.com/questions/696850/…
LukeH

예, 이것을 .ToDelimitedString 확장 메소드로 쉽게 작성할 수 있습니다. 나는 한 줄짜리 문자열로 갈 것입니다 .StringBuilder를 사용하여 마지막 문자를 트리밍하는 대신 하나를 가입하십시오.
Keith

3

ToArray를 사용하여 IList를 배열로 변환 한 다음 배열에서 string.join 명령을 실행할 수 있습니다.

Dim strs As New List(Of String)
Dim arr As Array
arr = strs.ToArray

3

.NET 3.5의 Linq 확장을 사용하여 배열로 쉽게 변환 할 수 있습니다.

   var stringArray = stringList.ToArray();

3

다른 방법으로 나열된 메소드 중 하나를 사용하여 배열로 변환 한 후 다음과 같은 것을 사용할 수도 있습니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Net;
using System.Configuration;

namespace ConsoleApplication
{
    class Program
    {
        static void Main(string[] args)
        {
            CommaDelimitedStringCollection commaStr = new CommaDelimitedStringCollection();
            string[] itemList = { "Test1", "Test2", "Test3" };
            commaStr.AddRange(itemList);
            Console.WriteLine(commaStr.ToString()); //Outputs Test1,Test2,Test3
            Console.ReadLine();
        }
    }
}

편집 : 여기 또 다른 예가 있습니다.


3

이 기사에서 발생하기 전에이 문제를 해결했습니다. 내 솔루션은 다음과 같습니다.

   private static string GetSeparator<T>(IList<T> list, T item)
   {
       return (list.IndexOf(item) == list.Count - 1) ? "" : ", ";
   }

다음과 같이 호출됩니다.

List<thing> myThings;
string tidyString;

foreach (var thing in myThings)
{
     tidyString += string.format("Thing {0} is a {1}", thing.id, thing.name) + GetSeparator(myThings, thing);
}

나는 또한 그렇게 쉽게 표현할 수 있었고 더 효율적이었을 것이다.

string.Join(“,”, myThings.Select(t => string.format(“Thing {0} is a {1}”, t.id, t.name)); 

3

내 대답은 위의 집계 솔루션과 같지만 명시 적 대리자 호출이 없으므로 호출 스택이 적어야합니다.

public static string ToCommaDelimitedString<T>(this IEnumerable<T> items)
{
    StringBuilder sb = new StringBuilder();
    foreach (var item in items)
    {
        sb.Append(item.ToString());
        sb.Append(',');
    }
    if (sb.Length >= 1) sb.Length--;
    return sb.ToString();
}

물론, 서명자를 구분자 독립적으로 확장 할 수 있습니다. 나는 실제로 sb.Remove () 호출의 팬이 아니며 IEnumerable을 통해 직선 루프 동안 리팩터링하고 MoveNext ()를 사용하여 쉼표를 쓸지 여부를 결정하고 싶습니다. 내가 해결책을 찾게되면 그 해결책을 게시하고 게시 할 것입니다.


처음에 내가 원하는 것은 다음과 같습니다.

public static string ToDelimitedString<T>(this IEnumerable<T> source, string delimiter, Func<T, string> converter)
{
    StringBuilder sb = new StringBuilder();
    var en = source.GetEnumerator();
    bool notdone = en.MoveNext();
    while (notdone)
    {
        sb.Append(converter(en.Current));
        notdone = en.MoveNext();
        if (notdone) sb.Append(delimiter);
    }
    return sb.ToString();
}

없음 임시 배열 또는 목록 저장이 필요하지 않으며 StringBuilder Remove()또는 Length--필요 해킹에서.

내 프레임 워크 라이브러리 에서이 메소드 서명을 약간 변형 delimiter하여 converter매개 변수를 포함 하고 매개 변수를 각각 사용 ","하고 x.ToString()기본값으로 사용했습니다.


3

잘만되면 이것이 가장 간단한 방법입니다

 string Commaseplist;
 string[] itemList = { "Test1", "Test2", "Test3" };
 Commaseplist = string.join(",",itemList);
 Console.WriteLine(Commaseplist); //Outputs Test1,Test2,Test3

3

MySql 메서드와 같이 문자열을 조인하는 좋은 C # 메서드를 검색하는 동안이 토론을 진행했습니다 CONCAT_WS(). 이 방법은 string.Join()문자열이 NULL이거나 비어있는 경우 구분 기호를 추가하지 않는다는 점에서 방법과 다릅니다 .

CONCAT_WS ( ',', tbl. 성, tbl. 이름)

Lastname이름이 비어있는 경우 에만 반환됩니다.

string.Join ( ",", strLastname, strFirstname)

strLastname + ", "같은 경우에 반환 됩니다.

첫 번째 행동을 원하면서 다음과 같은 방법을 작성했습니다.

    public static string JoinStringsIfNotNullOrEmpty(string strSeparator, string strA, string strB, string strC = "")
    {
        return JoinStringsIfNotNullOrEmpty(strSeparator, new[] {strA, strB, strC});
    }

    public static string JoinStringsIfNotNullOrEmpty(string strSeparator, string[] arrayStrings)
    {
        if (strSeparator == null)
            strSeparator = "";
        if (arrayStrings == null)
            return "";
        string strRetVal = arrayStrings.Where(str => !string.IsNullOrEmpty(str)).Aggregate("", (current, str) => current + (str + strSeparator));
        int trimEndStartIndex = strRetVal.Length - strSeparator.Length;
        if (trimEndStartIndex>0)
            strRetVal = strRetVal.Remove(trimEndStartIndex);
        return strRetVal;
    }

2

효율적인 방법으로 몇 가지 확장 방법을 작성했습니다.

    public static string JoinWithDelimiter(this IEnumerable<String> that, string delim) {
        var sb = new StringBuilder();
        foreach (var s in that) {
            sb.AppendToList(s,delim);
        }

        return sb.ToString();
    }

이것은에 따라

    public static string AppendToList(this String s, string item, string delim) {
        if (s.Length == 0) {
            return item;
        }

        return s+delim+item;
    }

3
+ 연산자를 사용하여 문자열을 연결하면 매번 새 문자열이 할당되므로 좋지 않습니다. 더욱이 StringBuilder는 암시 적으로 문자열로 캐스트 될 수 있지만, 자주 반복하면 (반복 될 때마다) 문자열 작성기의 목적을 크게 상실하게됩니다.
Daniel Fortunov

2

당신이 사용할 수있는 .ToArray()on ListsIEnumerables을 사용한 다음 String.Join()원하는대로 .


0

결합하려는 문자열이 객체 목록에 있으면 다음과 같이 할 수 있습니다.

var studentNames = string.Join(", ", students.Select(x => x.name));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.