Programing

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

lottogame 2020. 3. 29. 08:39
반응형

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


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


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

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

유일한 문제 String.Join는 문자열을 공통 구분 기호로 연결해야한다는 것입니다. @ ryanversaw가 지적했듯이 (편집 :) 구분 기호 문자열을 만들 수 있습니다.

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

.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의 한 라인 +연결에서 수행 된 최적화 에 대해 자세히 설명합니다.


문자열 연결에는 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 메소드가 좋습니다.


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

엄지 손가락의 규칙

  • 세 개의 동적 문자열 값 이하를 연결하는 경우 기존 문자열 연결을 사용하십시오.

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

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

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


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

그러나 Rico Mariani (.NET 최적화 전문가) 대부분의 경우 String.Format을 권장한다고 말한 퀴즈 를 구성했습니다.


다음은 대규모 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


MSDN 기사에서 :

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

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


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

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

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

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

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

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


문자열 리터럴을+ 연결하는 경우 연산자 를 사용해야 함을 지적하는 것도 중요합니다 .

+ 연산자를 사용하여 문자열 리터럴 또는 문자열 상수를 연결하면 컴파일러는 단일 문자열을 만듭니다. 런타임 연결이 발생하지 않습니다.

방법 : 여러 문자열 연결 (C # 프로그래밍 가이드)


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

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

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

문자열 보간


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

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

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


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

 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을 사용하십시오. 단기 사용자 앱용 앱이 있다면 두 가지를 모두 사용할 수 있거나 두 번째는 개발자에게 더 "자연적"일 것입니다.

건배.


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


다른 해결책 :

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

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

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

매우 빠릅니다.


실제로는 사용 패턴에 따라 다릅니다. string.Join, string, Concat 및 string.Format 사이의 자세한 벤치 마크는 여기에서 찾을 수 있습니다. String.Format은 집중 로깅에 적합하지 않습니다.

(이것은 실제로 내가 준 같은 대답 질문)


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

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


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

참고 URL : https://stackoverflow.com/questions/21078/most-efficient-way-to-concatenate-strings

반응형