문제

C#의와 유사한 효율적인 문자열 연결 기능을 제공하는 C ++ 표준 템플릿 라이브러리 클래스가 있습니까? StringBuilder 또는 Java 's StringBuffer?

도움이 되었습니까?

해결책

이 답변은 최근에 약간의 관심을 받았습니다. 나는 이것을 해결책으로 옹호하지 않습니다 (그것은 과거에 STL 전에 본 솔루션입니다). 흥미로운 접근 방식이며 적용해야합니다. std::string 또는 std::stringstream 코드를 프로파일 링 한 후이를 발견하면 개선됩니다.

나는 보통 어느 쪽도 사용합니다 std::string 또는 std::stringstream. 나는 이것에 아무런 문제가 없었습니다. 줄의 거친 크기를 미리 알면 일반적으로 먼저 방을 예약합니다.

나는 다른 사람들이 먼 과거에 자신의 최적화 된 현악기를 만드는 것을 보았습니다.

class StringBuilder {
private:
    std::string main;
    std::string scratch;

    const std::string::size_type ScratchSize = 1024;  // or some other arbitrary number

public:
    StringBuilder & append(const std::string & str) {
        scratch.append(str);
        if (scratch.size() > ScratchSize) {
            main.append(scratch);
            scratch.resize(0);
        }
        return *this;
    }

    const std::string & str() {
        if (scratch.size() > 0) {
            main.append(scratch);
            scratch.resize(0);
        }
        return main;
    }
};

그것은 문자열의 대부분에 대해 하나의 문자열을 사용하고 다른 하나는 짧은 문자열을 연결하기위한 스크래치 영역으로 사용합니다. 하나의 작은 문자열로 짧은 부속 작업을 한 번에 대 한 다음 주 문자열에 추가하여 메인 문자열에 필요한 재 할당 수가 줄어들어 최적화를 최적화합니다.

나는이 트릭을 필요로하지 않았다 std::string 또는 std::stringstream. 나는 그것이 std :: string 전에 타사 문자열 라이브러리와 함께 사용되었다고 생각합니다. 이 프로필과 같은 전략을 채택한 경우 먼저 응용 프로그램을 사용하십시오.

다른 팁

C ++ 방법은 사용하는 것입니다 std :: stringstream 또는 단지 평범한 문자열 연결. C ++ 문자열은 변이 가능하므로 연결의 성능 고려 사항은 문제가되지 않습니다.

서식과 관련하여 스트림에서 동일한 형식을 모두 수행 할 수 있지만 다른 방식으로 cout. 또는이를 캡슐화하고 문자열을 제공하는 강력하게 입력 된 functor를 사용할 수 있습니다. 인터페이스와 같은 형식 예 : 부스트 :: 형식

std :: string.append 함수는 여러 형태의 데이터를 허용하지 않기 때문에 좋은 옵션이 아닙니다. 보다 유용한 대안은 STD : StringStream을 사용하는 것입니다.

#include <sstream>
// ...

std::stringstream ss;

//put arbitrary formatted data into the stream
ss << 4.5 << ", " << 4 << " whatever";

//convert the stream buffer into a string
std::string str = ss.str();

std::string ~이다 C ++ 등가 : 변이 가능합니다.

문자열을 연결하는 데 .append ()를 사용할 수 있습니다.

std::string s = "string1";
s.append("string2");

나는 당신이 할 수 있다고 생각합니다.

std::string s = "string1";
s += "string2";

C#의 서식 작업은 StringBuilder, 나는 믿는다 snprintf (또는 sprintf 버기 코드를 쓸 위험을 감수하려면 ;-)) 문자 배열로 다시 문자열로 변환하는 것이 유일한 옵션에 관한 것입니다.

부터 std::string C ++에서는 변이 가능합니다. 그것은 있습니다 += operator 그리고 append 기능.

수치 데이터를 추가 해야하는 경우 사용하십시오 std::to_string 기능.

문자열에 객체를 직렬화 할 수있는 형태로 더 많은 유연성을 원한다면 std::stringstream 수업. 그러나 자신의 커스텀 클래스와 함께 작동하려면 자체 스트리밍 연산자 기능을 구현해야합니다.

std :: string 's +=는 const char* ( "string to add"라는 것과 같은 것들)에서 작동하지 않으므로 StringStream을 사용하는 것은 필요한 것과 가장 가깝습니다. +대신 << <<

C ++의 편리한 문자열 빌더

많은 사람들이 전에 대답했던 것처럼 std :: stringstream이 선택한 방법입니다. 잘 작동하며 많은 변환 및 서식 옵션이 있습니다. IMO는 하나의 꽤 불편한 결함을 가지고 있습니다. 하나의 라이너 또는 표현으로 사용할 수 없습니다. 당신은 항상 다음을 써야합니다.

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );

특히 생성자의 문자열을 초기화하려는 경우 꽤 성가시다.

그 이유는 a) std :: stringstream이 std :: string에 대한 변환 연산자가 없기 때문입니다. b) 연산자 << () 's stringstream은 stringstream 참조를 반환하지 않지만 std :: ostream 참조를 대신에 반환합니다. - 문자열 스트림으로 더 계산할 수 없습니다.

해결책은 std :: stringstream을 무시하고 더 잘 일치하는 연산자를 제공하는 것입니다.

namespace NsStringBuilder {
template<typename T> class basic_stringstream : public std::basic_stringstream<T>
{
public:
    basic_stringstream() {}

    operator const std::basic_string<T> () const                                { return std::basic_stringstream<T>::str();                     }
    basic_stringstream<T>& operator<<   (bool _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (char _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (signed char _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned char _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (short _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned short _val)                   { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (int _val)                              { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned int _val)                     { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long long _val)                        { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long long _val)               { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (float _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (double _val)                           { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long double _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (void* _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::streambuf* _val)                  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ostream& (*_val)(std::ostream&))  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios& (*_val)(std::ios&))          { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios_base& (*_val)(std::ios_base&)){ std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (const T* _val)                         { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val)); }
    basic_stringstream<T>& operator<<   (const std::basic_string<T>& _val)      { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val.c_str())); }
};

typedef basic_stringstream<char>        stringstream;
typedef basic_stringstream<wchar_t>     wstringstream;
}

이것으로, 당신은 같은 것을 쓸 수 있습니다

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )

생성자에서도.

나는 성능을 측정하지 않았다고 고백해야한다. 성능을 측정하지 않았다고 고백해야한다. 왜냐하면 아직 문자열 건물을 많이 사용하는 환경에서 사용하지 않았기 때문이다. 참조를 통해 (문자열로 변환을 제외하고, 그러나 그것은 std :: stringstream의 사본 조작입니다)

그만큼 로프 컨테이너는 문자열의 임의의 장소에 문자열을 삽입/삭제 해야하는 경우 또는 긴 숯 시퀀스를 위해 가치가있을 수 있습니다. 다음은 SGI 구현의 예입니다.

crope r(1000000, 'x');          // crope is rope<char>. wrope is rope<wchar_t>
                                // Builds a rope containing a million 'x's.
                                // Takes much less than a MB, since the
                                // different pieces are shared.
crope r2 = r + "abc" + r;       // concatenation; takes on the order of 100s
                                // of machine instructions; fast
crope r3 = r2.substr(1000000, 3);       // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
                                // correct, but slow; may take a
                                // minute or more.

다음으로 인해 새로운 것을 추가하고 싶었습니다.

첫 번째 시도에서 나는 이겼다

std::ostringstream '에스 operator<<

효율성이지만 더 많은 Attemps를 사용하면 경우에 따라 더 빠른 StringBuilder를 만들 수있었습니다.

문자열을 추가 할 때마다 어딘가에 대한 참조를 저장하고 총 크기의 카운터를 늘립니다.

내가 마침내 그것을 구현 한 진짜 방법 (공포!)은 불투명 버퍼 (std :: vector <char>)를 사용하는 것입니다.

  • 1 바이트 헤더 (2 비트 (2 비트)는 다음 데이터가 다음과 같은지 알려줍니다 : 이동 문자열, 문자열 또는 바이트 [])
  • 바이트의 Lenght에게 말하는 6 비트 [

바이트 [

  • 나는 짧은 줄의 직접 바이트를 저장합니다 (순차적 메모리 액세스 용)

움직이는 현 (문자열이 추가되었습니다 std::move)

  • A에 대한 포인터 std::string 개체 (우리는 소유권이 있습니다)
  • 미사용 예약 바이트가있는 경우 클래스에 깃발을 설정하십시오.

현악기

  • A에 대한 포인터 std::string 개체 (소유권 없음)

마지막으로 삽입 된 문자열이 이동 한 경우 하나의 작은 최적화도 있습니다. 무료 예약되었지만 사용하지 않은 바이트를 확인하고 불투명 버퍼를 사용하는 대신 추가 바이트를 저장합니다 (이것은 일부 메모리를 저장하기 위해서는 실제로 약간 느리게 만듭니다. , 아마도 CPU에도 의존 할 수도 있고 어쨌든 추가 공간이있는 줄을 보는 것은 드 rare니다).

이것은 마침내 약간 더 빠릅니다 std::ostringstream 그러나 단점은 거의 없습니다.

  • 나는 고정 된 lenght char 유형 (따라서 1,2 또는 4 바이트, UTF8에 좋지 않음)을 가정했다. 나는 그것이 UTF8에서는 효과가 없다고 말하지 않고 게으름을 확인하지 않았다.
  • 나는 나쁜 코딩 연습을 사용했습니다 (불투명 버퍼, 휴대용을 쉽게 만들 수 있습니다. 내 것이 휴대가 가능하다고 생각합니다)
  • 모든 기능이 부족합니다 ostringstream
  • 일부 문자열이 삭제되면 모든 문자열이 정의되지 않은 동작.

결론? 사용std::ostringstream

이미 가장 큰 병목 현상을 수정하는 반면, 광산 구현으로 속도가 적은 포인트를 늘리는 것은 다운 사이드의 가치가 없습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top