문자열을 연결하는 가장 효율적인 방법은 무엇입니까?


286

문자열을 연결하는 가장 효율적인 방법은 무엇입니까?


9
여기에 모든 관련 사례를 논의하지 않기 때문에 허용되는 답변이 상당히 불완전하다는 경고를 여기에 표시하고 싶습니다.
usr

@usr 실제로 ... StringBuilder사용 사례 에 대한 자세한 정보는 여기를 참조하십시오 .
Tamir Vered

C # 6에서 내가 가장 좋아하는 것은 $ "Constant text here {foo} and {bar}" String.Format입니다. 스테로이드 와 같습니다 . 어떤 성능 현명,보다 라이너에 작은 조금 느린 +String.Concat이기는하지만보다 느리게,하지만 훨씬 더보다 StringBuilder다수의 호출에. 실제로, 성능 차이는 연결하는 한 가지 방법 만 선택해야하는 $경우 다음을 사용하여 문자열 보간을 선택합니다 . 두 가지 방법으로 StringBuilder도구 상자에 추가 합니다. 이 두 가지 방법으로 설정됩니다.
u8it

String.Join아래 의 대답은 +정의를 하지 않으며 실제로 말하면 문자열을 연결하는 나쁜 방법이지만 놀랍게도 성능이 빠릅니다. 답은 왜 흥미로운가. String.Concat그리고 String.Join두 배열에 따라 행동 할 수 있지만, String.Join실제로는 더 빠르다. 분명히 문자열보다 먼저 문자열 길이를 계산 한 다음 UnSafeCharBuffer를 사용하여이 지식으로부터 이익을 얻는 문자열을 구성한다는 점 과 비슷하게 작동하기 때문에 String.Join보다 정교하고 최적화 되어 있습니다. String.ConcatStringBuilder
u8it

좋아, 그래서 빨리, 그러나 String.Join또한 자원 비효율적 인 권리를 보인다 배열을 구성 할 필요? ... 밝혀 +String.Concat그 성분에 대한 구조 배열 어쨌든. 따라서, 수동 배열을 생성하고 그것을 위해 먹이 String.Join하지만 ... 비교적 빠른 StringBuilder여전히 능가 String.Join하면서 모든 실용적인 방법에 대한에 $약간 느리고 훨씬 빠르게 긴 문자열에서입니다 ... 그것을 사용 어색하고 추한 것을 언급하지 않기 위하여 String.Join당신이있는 경우 그 자리에 배열을 만들 수 있습니다.
u8it

답변:


155

StringBuilder.Append()방법은 +연산자를 사용하는 것보다 훨씬 낫습니다 . 그러나 1000 개 이하의 연결을 실행할 String.Join()때보 다 훨씬 효율적 이라는 것을 알았 습니다 StringBuilder.

StringBuilder sb = new StringBuilder();
sb.Append(someString);

유일한 문제 String.Join는 문자열을 공통 구분 기호로 연결해야한다는 것입니다.

편집 :@ryanversaw는 지적, 당신은 구분 기호를 만들 수 있습니다 string.Empty.

string key = String.Join("_", new String[] 
{ "Customers_Contacts", customerID, database, SessionID });

11
StringBuilder시작 비용이 엄청나게 크고, 매우 큰 문자열이나 매우 많은 연결과 함께 사용할 때만 효율적입니다. 주어진 상황에 대해 알아내는 것은 쉬운 일이 아닙니다. 성능에 문제가 있으면 프로파일 링이 친구입니다 (ANTS 확인).
Abel

32
단일 회선 연결에는 해당되지 않습니다. myString = "foo"+ var1 + "bar"+ var2 + "hello"+ var3 + "world"를 수행한다고 가정하면 컴파일러는이를 자동으로 string.concat 호출로 변환합니다. 이 답변은 잘못되었습니다. 더 나은 답변을 선택할 수 있습니다.
csauve

2
사소한 문자열 연결을 위해 가장 읽기 쉬운 것을 사용하십시오. 문자열 a = b + c + d; StringBuilder를 사용하는 것보다 거의 항상 빠르지 만 차이점은 일반적으로 관련이 없습니다. 동일한 문자열에 반복적으로 추가 (예 : 보고서 작성)하거나 큰 문자열을 처리 할 때는 StringBuilder (또는 선택한 다른 옵션)를 사용하십시오.
Swanny

5
왜 언급하지 않았 string.Concat습니까?
Venemo

272

.NET 성능 전문가 인 Rico Mariani 는이 주제에 대한 기사 를 가지고있었습니다 . 생각하는 것만 큼 간단하지 않습니다. 기본 조언은 다음과 같습니다.

패턴이 다음과 같은 경우 :

x = f1(...) + f2(...) + f3(...) + f4(...)

그것은 하나의 concat이고 zippy입니다 .StringBuilder는 아마 도움이되지 않을 것입니다.

패턴이 다음과 같은 경우 :

if (...) x += f1(...)
if (...) x += f2(...)
if (...) x += f3(...)
if (...) x += f4(...)

그렇다면 아마도 StringBuilder를 원할 것입니다.

이 주장을 뒷받침하는 또 다른 기사 는 Eric Lippert의 한 라인 +연결에서 수행 된 최적화 에 대해 자세히 설명합니다.


1
String.Format ()은 어떻습니까?
IronSlug

86

문자열 연결에는 6 가지 유형이 있습니다.

  1. 더하기 ( +) 기호 사용.
  2. 사용 string.Concat().
  3. 사용 string.Join().
  4. 사용 string.Format().
  5. 사용 string.Append().
  6. 사용 StringBuilder.

실험 string.Concat()에서 단어가 1000 (약)보다 적 으면 단어가 1000보다 크면 접근하는 것이 가장 좋은 방법 이라는 것이 입증되었습니다 StringBuilder.

자세한 내용은이 사이트를 확인 하십시오 .

string.Join () vs. string.Concat ()

여기서 string.Concat 메소드는 빈 구분 기호가있는 string.Join 메소드 호출과 같습니다. 빈 문자열을 추가하는 것은 빠르지 만 그렇게하지 않으면 더 빠르므로 string.Concat 메소드가 더 좋습니다.


4
읽어야합니다 .string.Concat () 또는 +가 가장 좋습니다. 예, 기사 에서이 정보를 얻을 수 있지만 한 번의 클릭으로 저장됩니다. 따라서 +와 concat은 동일한 코드로 컴파일됩니다.
brumScouse

나는이 기초를 사용하여 내 방법을보다 효율적으로 만들려고 노력했습니다. 여기서 정확히 3 개의 현만 연결하면됩니다. 나는 아웃 레이싱 전에 필요한 문자열의 양을 조사하지는 않았지만 +실제로는 3 밀리 초보다 빠르다는 것을 알았습니다 . string.Concat()string.Concat()+
Gnemlock

59

에서 Chinh 금지 - StringBuilder에 항상 빠른되지 않습니다 :

엄지 손가락의 규칙

  • 합치 할 때 미만 동적 문자열 값을하거나, 기존의 문자열 연결을 사용합니다.

  • 세 개 이상의 동적 문자열 값을 연결할 때는을 사용하십시오 StringBuilder.

  • 여러 문자열 리터럴에서 큰 문자열을 작성할 때는 @문자열 리터럴 또는 인라인 + 연산자를 사용하십시오.

대부분 의 경우 StringBuilder가장 좋은 방법이지만, 그 게시물에 표시된 것처럼 적어도 각 상황에 대해 생각해야 할 경우가 있습니다.


8
afaik @는 이스케이프 시퀀스 처리 만 해제합니다. msdn.microsoft.com/ko-kr/library/362314fe.aspx 동의
abatishchev

12

루프에서 작업하는 경우 StringBuilder갈 수있는 방법 일 것입니다. 정기적으로 새 문자열을 작성하는 오버 헤드를 줄입니다. 그러나 한 번만 실행되는 코드에서는 String.Concat아마 좋습니다.

그러나 Rico Mariani (.NET 최적화 전문가) 마지막에 대부분의 경우 추천 하는 퀴즈 를 구성했습니다 String.Format.


몇 년 동안 함께 일한 사람들에게 string + string 대신 string.format을 사용하는 것이 좋습니다. 가독성의 이점은 성능상의 이점을 넘어서는 추가적인 이점이라고 생각합니다.
Scott Lawrence

1
이것이 실제 정답입니다. StringBuilder에 대해 현재 허용되는 답변은 string.concat 또는 +가 더 빠른 단일 행 추가를 언급하지 않으므로 올바르지 않습니다. 알려진 사실은 컴파일러가 실제로 +를 string.concat으로 변환한다는 것입니다. 나는 사용자 정의 내장 문자열 빌더를 사용하여 루프 또는 여러 라인 concats에 대한로 .toString가 불려 갔을 때에 만이 추가하는 -의 StringBuilder가 가지고있는 불확정 버퍼 문제를 극복
csauve

2
string.Format은 어떤 상황에서도 가장 빠른 방법이 아닙니다. 앞으로 일어날 사건을 해결하는 방법을 모르겠습니다.
usr

@usr-Rico는 명시 적으로 가장 빠르다 는 말이 아니라 그의 추천이라고 말합니다. "최악의 성능이라하더라도 CLR 성능 설계자 모두 [string.Format]은 다음과 같이 동의합니다. 퍼포먼스 문제가 될 가능성이 거의없는 경우, 약간의 로컬 변경만으로 문제를 쉽게 해결할 수 있습니다. 일반적으로 유지 관리가 용이합니다. "
Adam V

@AdamV 질문은 가장 빠른 방법에 관한 것입니다. 성능상의 이유로는 아니지만 기본 선택에 동의하지 않습니다. 서투른 구문 일 수 있습니다. Resharper는 마음대로 앞뒤로 변환 할 수 있습니다.
usr

10

다음은 대규모 NLP 앱에서 10 년 동안 발전한 가장 빠른 방법입니다. IEnumerable<T>다른 유형 ( Char, String)의 구분 기호 가 있거나없는 변형 과 다른 입력 유형이 있지만 배열의 모든 문자열을 구분 기호가없는 단일 문자열로 연결 하는 간단한 경우를 보여줍니다 . 최신 버전은 C # 7.NET 4.7 에서 개발되고 단위 테스트되었습니다 .

성능을 높이려면 두 가지 열쇠가 있습니다. 첫 번째는 필요한 정확한 총 크기를 미리 계산하는 것입니다. 이 단계는 입력이 여기에 표시된 배열 인 경우에는 간단합니다. IEnumerable<T>대신 처리 하려면 먼저 총계를 계산하기 위해 문자열을 임시 배열로 수집하는 ToString()것이 좋습니다. 부작용이 발생할 가능성이 있기 때문에 기술적으로 요소 당 두 번 이상 호출하는 것을 피해야 합니다. '문자열 조인'작업).

다음으로 최종 문자열의 총 할당 크기를 고려할 때 결과 문자열을 제자리구축하면 성능이 크게 향상됩니다 . 이를 위해서는 String처음에는 0으로 할당 된 새로운 불변성을 일시적으로 중단시키는 (아마도 논쟁의 여지가있는) 기술이 필요합니다 . 그러나 그러한 논쟁은 제쳐두고 ...

... 이것은 생성자에 의한 여분의 할당 및 복사 를 완전히 피하는이 페이지의 유일한 대량 연결 솔루션입니다 String.

완전한 코드 :

/// <summary>
/// Concatenate the strings in 'rg', none of which may be null, into a single String.
/// </summary>
public static unsafe String StringJoin(this String[] rg)
{
    int i;
    if (rg == null || (i = rg.Length) == 0)
        return String.Empty;

    if (i == 1)
        return rg[0];

    String s, t;
    int cch = 0;
    do
        cch += rg[--i].Length;
    while (i > 0);
    if (cch == 0)
        return String.Empty;

    i = rg.Length;
    fixed (Char* _p = (s = new String(default(Char), cch)))
    {
        Char* pDst = _p + cch;
        do
            if ((t = rg[--i]).Length > 0)
                fixed (Char* pSrc = t)
                    memcpy(pDst -= t.Length, pSrc, (UIntPtr)(t.Length << 1));
        while (pDst > _p);
    }
    return s;
}

[DllImport("MSVCR120_CLR0400", CallingConvention = CallingConvention.Cdecl)]
static extern unsafe void* memcpy(void* dest, void* src, UIntPtr cb);

이 코드는 내가 사용하는 것에서 약간 수정되었다는 것을 언급해야합니다. 원본에서는 실제 복사를 수행하기 위해 C # 에서 cpblk IL 명령어호출합니다 . 여기서 코드의 단순성과 이식성을 위해, 보시 다시피 대신 P / Invoke로 대체했습니다 . x64에서 성능을 높이 려면 ( x86아님) 대신 cpblk 방법 을 사용하는 것이 좋습니다.memcpy


string.Join이 모든 것들이 이미 당신을 위해합니다. 직접 작성할 필요가 없습니다. 최종 문자열의 크기를 계산하고 해당 크기의 문자열을 구성한 다음 기본 문자 배열에 씁니다. 프로세스에서 읽을 수있는 변수 이름을 사용하는 보너스도 있습니다.
Servy

1
@Servy 의견에 감사드립니다; 실제로 String.Join효율적일 수 있습니다. 소개에서 암시 한 것처럼, 여기에있는 코드는 String.Join처리하지 않거나 (예 : Char구분 기호 최적화와 같은 ) 이전 버전의 .NET에서 처리하지 않은 시나리오에 사용하는 함수 제품군의 가장 간단한 그림입니다 . 나는 가장 간단한 예를 위해 이것을 선택해서는 안된다고 생각합니다. 왜냐하면 String.Join"비 효율성"은 측정 할 수 없지만 아마도 빈 분리기를 처리 할 수는 있지만 이미 잘 처리 된 경우이기 때문입니다 . String.Empty.
Glenn Slayden

물론, 당신이 구분을하지 않아도 경우에, 당신은 호출해야 Concat하는, 또한 올바르게이 작업을 수행합니다. 어느 쪽이든 직접 코드를 작성할 필요는 없습니다.
Servy

7
@Servy 이 테스트 하네스를String.Join 사용하여 코드 성능 과 코드 성능을 비교했습니다 . 100 단어 크기의 문자열을 각 최대 1 개 천만 임의 연결 작업의 경우, 위의 코드는 34 %보다 더 빨리 일관하다 에 의 x64 와 릴리스 빌드 .NET 4.7 . OP가 명시 적으로 "가장 효율적인"방법을 요청하기 때문에 결과는 내 대답이 적용됨을 나타냅니다. 이것이 귀하의 우려를 해결하는 경우, 귀하는 귀하의 공감대를 재고 할 것을 요청합니다. String.Join
Glenn Slayden

1
최근 64 전체 CLR 4.7.1에서이를 벤치마킹하고 약 25 % 이하 (할당 된 메모리에 대한 배 빠른 string.Join 등으로 찾아 i.imgur.com/SxIpEmL.png cpblk 또는 사용하는 경우) github.com/를 JonHanna / Mnemosyne
quentin-starin

6

MSDN 기사에서 :

시간과 메모리 모두에서 StringBuilder 객체를 만드는 것과 관련된 약간의 오버 헤드가 있습니다. 메모리가 빠른 컴퓨터에서는 약 5 개의 작업을 수행하는 경우 StringBuilder가 유용합니다. 경험상, 10 개 이상의 문자열 연산은 어느 머신에서나 오버 헤드에 대한 오버 헤드에 대한 정당화라고 말합니다.

따라서 10 개 이상의 문자열 작업 / 연결을 수행 해야하는 경우 MSDN을 StringBuilder와 함께 사용한다고 신뢰하면 '+'로 간단한 문자열 연결이 좋습니다.



5

다른 답변에 덧붙여 StringBuilder는 할당 할 초기 메모리 양을 알 수 있습니다 .

용량 파라미터는 현재 인스턴스에 할당 된 메모리에 저장 될 수있는 최대 문자 수를 정의한다. 해당 값은 Capacity 속성에 할당됩니다 . 현재 인스턴스에 저장 될 문자 수가이 용량 값을 초과 하면 StringBuilder 객체는 이 를 저장하기 위해 추가 메모리를 할당합니다.

경우 용량이 제로, 구현 고유의 기본 용량이 사용됩니다.

사전 할당되지 않은 StringBuilder에 반복적으로 추가하면 일반 문자열을 반복적으로 연결하는 것처럼 불필요한 할당이 많이 발생할 수 있습니다.

최종 문자열의 길이를 알고 있거나, 사소하게 계산하거나, 일반적인 경우에 대해 교육적인 추측을 할 수있는 경우 (너무 많이 할당하는 것이 반드시 나쁜 것은 아님),이 정보를 생성자 또는 용량 속성. 특히 성능 테스트를 실행하여 StringBuilder와 내부적으로 동일한 기능을 수행하는 String.Concat과 같은 다른 메소드를 비교할 때. 온라인에서 당신이 비교할 때 StringBuilder 사전 할당이 포함되지 않은 테스트는 잘못되었습니다.

크기에 대해 추측 할 수 없다면 사전 할당 제어를위한 자체 선택적 인수가 있어야하는 유틸리티 함수를 작성하는 것입니다.


4

다음은 여러 문자열을 연결하는 하나 이상의 대체 솔루션입니다.

String str1 = "sometext";
string str2 = "some other text";

string afterConcate = $"{str1}{str2}";

문자열 보간


1
이것은 일반적인 연결 방법으로 실제로 놀랍습니다. 기본적으로 String.Format읽기 쉽고 작업하기가 더 쉽습니다. 벤치 마크는, 그것보다 약간 느리다 +String.Concat한 줄의 회씩 연결에서하지만 훨씬 더 그 양에 비해 제작 반복적 인 통화에서 StringBuilder덜 필요합니다.
u8it

2

가장 효율적인 방법은 다음과 같이 StringBuilder를 사용하는 것입니다.

StringBuilder sb = new StringBuilder();
sb.Append("string1");
sb.Append("string2");
...etc...
String strResult = sb.ToString();

@ jonezy : String.Concat은 몇 가지 작은 것들이 있으면 좋습니다. 그러나 메가 바이트의 데이터를 연결하는 경우 프로그램이 중단 될 수 있습니다.


메가 바이트 단위의 데이터 솔루션은 무엇입니까?
Neel

2

이 두 조각의 코드를 시도하면 솔루션을 찾을 수 있습니다.

 static void Main(string[] args)
    {
        StringBuilder s = new StringBuilder();
        for (int i = 0; i < 10000000; i++)
        {
            s.Append( i.ToString());
        }
        Console.Write("End");
        Console.Read();
    }

static void Main(string[] args)
    {
        string s = "";
        for (int i = 0; i < 10000000; i++)
        {
            s += i.ToString();
        }
        Console.Write("End");
        Console.Read();
    }

첫 번째 코드는 정말 빨리 끝나고 메모리는 충분할 것입니다.

두 번째 코드는 메모리에 문제가 없지만 시간이 더 오래 걸립니다. 따라서 많은 사용자를위한 응용 프로그램이 있고 속도가 필요한 경우 1을 사용하십시오. 단기 사용자 앱용 앱이 있다면 두 가지를 모두 사용할 수 있거나 두 번째는 개발자에게 더 "자연적"일 것입니다.

건배.


1

두 개의 문자열에 대해서만 StringBuilder를 사용하고 싶지 않습니다. StringBuilder 오버 헤드가 여러 문자열을 할당하는 오버 헤드보다 작은 임계 값이 있습니다.

따라서 2-3 개가 넘는 문자열에는 DannySmurf의 코드를 사용하십시오 . 그렇지 않으면 + 연산자를 사용하십시오.


1

System.String은 변경할 수 없습니다. 문자열 변수의 값을 수정하면 새 메모리가 새 값에 할당되고 이전 메모리 할당이 해제됩니다. System.StringBuilder는 수정 된 문자열에 대해 별도의 메모리 위치를 할당하지 않고도 다양한 작업을 수행 할 수있는 변경 가능한 문자열 개념을 갖도록 설계되었습니다.


1

다른 해결책 :

루프 내에서 문자열 대신 List를 사용하십시오.

List<string> lst= new List<string>();

for(int i=0; i<100000; i++){
    ...........
    lst.Add(...);
}
return String.Join("", lst.ToArray());;

매우 빠릅니다.



0

코드에 따라 다릅니다. StringBuilder는 일반적으로 더 효율적이지만, 몇 개의 문자열 만 연결하고 한 줄로 모든 작업을 수행하는 경우 코드 최적화가이를 대신 할 것입니다. 코드가 어떻게 보이는지에 대해 생각하는 것이 중요합니다. 더 큰 세트의 경우 StringBuilder는 읽기가 더 쉽고 작은 세트의 경우 StringBuilder는 불필요한 혼란을 추가합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.