null이 포함 된 std :: string을 어떻게 구성합니까?


89

다음과 같은 줄로 std :: string을 생성하려면

std::string my_string("a\0b");

결과 문자열 (a, null, b)에 세 문자를 포함하고 싶은 경우 하나만 얻습니다. 적절한 구문은 무엇입니까?


4
이것에주의해야합니다. 'b'를 숫자로 바꾸면 자동으로 잘못된 문자열이 생성됩니다. 참조 : stackoverflow.com/questions/10220401/...
— 데이비드 스톤

답변:


129

C ++ 14 이후

우리는 리터럴을 만들 수있었습니다 std::string

#include <iostream>
#include <string>

int main()
{
    using namespace std::string_literals;

    std::string s = "pl-\0-op"s;    // <- Notice the "s" at the end
                                    // This is a std::string literal not
                                    // a C-String literal.
    std::cout << s << "\n";
}

C ++ 14 이전

문제는 입력이 C- 문자열이라고 가정 하는 std::string생성자입니다 const char*. C- 문자열은 \0종료되므로 \0문자에 도달하면 구문 분석이 중지됩니다 .

이를 보완하려면 C-String이 아닌 char 배열에서 문자열을 작성하는 생성자를 사용해야합니다. 여기에는 배열에 대한 포인터와 길이의 두 매개 변수가 필요합니다.

std::string   x("pq\0rs");   // Two characters because input assumed to be C-String
std::string   x("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.

참고 : C ++ std::string는 종료 되지 않습니다 \0 (다른 게시물에서 제 안됨). 그러나 메서드를 사용하여 C-String을 포함하는 내부 버퍼에 대한 포인터를 추출 할 수 있습니다 c_str().

사용에 대한 Doug T의 답변을 아래 에서 확인하십시오 vector<char>.

또한 RiaD 에서 C ++ 14 솔루션을 확인하십시오 .


8
업데이트 : C ++ 11부터 문자열은 null로 종료됩니다. 즉, Loki의 게시물은 유효합니다.
— matthewaveryusa

14
@mna : 저장소 측면에서 null로 종료되지만, 용어의 일반적인 의미 인 의미있는 null 종료 (즉, 문자열 길이 정의 의미) 로 null로 종료된다는 의미에서는 아닙니다 .
— 궤도의 가벼운 경주

잘 설명했습니다. 감사합니다.
— JOMA

22

c 스타일 문자열 (문자 배열)과 같은 조작을 수행하는 경우 다음을 사용하는 것이 좋습니다.

std::vector<char>

c- 문자열을 처리하는 것과 같은 방식으로 배열처럼 처리 할 수있는 더 많은 자유가 있습니다. copy ()를 사용하여 문자열로 복사 할 수 있습니다.

std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());

c- 문자열을 사용할 수있는 동일한 위치에서 사용할 수 있습니다.

printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';

그러나 당연히 c- 문자열과 동일한 문제가 발생합니다. 널 터미널을 잊거나 할당 된 공간을 지나서 쓸 수 있습니다.


바이트를 문자열로 인코딩하려는 경우 (grpc 바이트는 문자열로 저장 됨) 대답에 지정된 벡터 메서드를 사용하십시오. 하지 일반적인 방법으로 전체 문자열을 구성하지 것이다 (아래 참조) byte *bytes = new byte[dataSize]; std::memcpy(bytes, image.data, dataSize * sizeof(byte)); std::string test(reinterpret_cast<char *>(bytes)); std::cout << "Encoded String length " << test.length() << std::endl;
— 알렉스 Punnen

13

나는 아무 생각이 없다 왜 당신이 그런 일을 할 수 있지만,이 시도 할 것을 :

std::string my_string("a\0b", 3);

1
이 작업에 대한 우려 사항은 무엇입니까? "a \ 0b"를 저장할 필요가 있는지 궁금하십니까? 또는 그러한 저장을 위해 std :: string 사용에 의문을 제기합니까? 후자의 경우 대안으로 무엇을 제안합니까?
— Anthony Cramp

3
@Constantin 그러면 바이너리 데이터를 문자열로 저장하는 경우 뭔가 잘못하고 있습니다. 그것이 무엇을 위해 vector<unsigned char>또는 unsigned char *발명되었습니다.
— Mahmoud Al-Qudsi

2
나는 문자열의 보안에 대해 더 많이 배우려고 노력하면서 이것을 발견했습니다. 파일 / 네트워크에서 텍스트 데이터로 예상되는 내용을 읽는 동안 null 문자를 읽어도 여전히 작동하는지 확인하기 위해 코드를 테스트하고 싶었습니다. std::string데이터가 일반 텍스트로 간주되어야 함을 나타 내기 위해 사용 하지만 일부 해싱 작업을 수행하고 있으며 모든 것이 여전히 null 문자와 함께 작동하는지 확인하고 싶습니다. 널 문자가 포함 된 문자열 리터럴의 유효한 사용처럼 보입니다.
— David Stone

3
@DuckMaestro 아니요, 사실이 아닙니다. \0UTF-8 문자열 의 바이트는 NUL 만 될 수 있습니다. 멀티 바이트로 인코딩 된 문자에는 \0해당 문제에 대한 다른 ASCII 문자도 포함되지 않습니다 .
— John Kugelman 2013 년

1
테스트 케이스에서 알고리즘을 유발하려고 할 때 이것을 발견했습니다. 따라서 타당한 이유가 있습니다. 비록 적지 만.
— namezero

12

사용자 정의 리터럴이 C ++에 추가하는 새로운 기능은 무엇입니까? 우아한 대답을 제시합니다. 정의

std::string operator "" _s(const char* str, size_t n) 
{ 
    return std::string(str, n); 
}

그런 다음 다음과 같이 문자열을 만들 수 있습니다.

std::string my_string("a\0b"_s);

또는 심지어 :

auto my_string = "a\0b"_s;

"오래된 스타일"방식이 있습니다.

#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string

그런 다음 정의 할 수 있습니다.

std::string my_string(S("a\0b"));

8

다음이 작동합니다 ...

std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');

대괄호로 묶인 괄호를 사용해야합니다.
— jk.

5

이것에주의해야합니다. 'b'를 숫자로 바꾸면 대부분의 방법을 사용하여 자동으로 잘못된 문자열을 만듭니다. 참조 : C ++ 문자열 리터럴 이스케이프 문자 규칙 .

예를 들어, 프로그램 중간에이 무고 해 보이는 스 니펫을 떨어 뜨 렸습니다.

// Create '\0' followed by '0' 40 times ;)
std::string str("\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", 80);
std::cerr << "Entering loop.\n";
for (char & c : str) {
    std::cerr << c;
    // 'Q' is way cooler than '\0' or '0'
    c = 'Q';
}
std::cerr << "\n";
for (char & c : str) {
    std::cerr << c;
}
std::cerr << "\n";

이 프로그램이 나에게 출력하는 내용은 다음과 같습니다.

Entering loop.
Entering loop.

vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ

그것은 두 번의 첫 번째 인쇄 문이었습니다. 몇 개의 인쇄되지 않는 문자와 줄 바꿈, 내부 메모리의 내용이 뒤따 랐습니다. 이는 방금 덮어 썼습니다 (덮어 썼다는 것을 보여주는 인쇄). 무엇보다도,이를 철저하고 장황한 gcc 경고로 컴파일해도 문제가 있음을 알 수 없었으며 valgrind를 통해 프로그램을 실행해도 부적절한 메모리 액세스 패턴에 대해 불평하지 않았습니다. 즉, 최신 도구로는 완전히 감지 할 수 없습니다.

훨씬 더 간단한으로도 동일한 문제를 얻을 수 std::string("0", 100);있지만 위의 예는 조금 더 까다로워서 무엇이 잘못되었는지 확인하기가 더 어렵습니다.

다행히 C ++ 11은 이니셜 라이저 목록 구문을 사용하여 문제에 대한 좋은 해결책을 제공합니다. 이렇게하면 문자 수를 지정하지 않아도되고 (위에서 보여 드린대로 잘못 수행 할 수 있음) 이스케이프 된 숫자 조합을 피할 수 있습니다. std::string str({'a', '\0', 'b'})배열 char및 크기 를 사용하는 버전과 달리 모든 문자열 콘텐츠에 안전합니다 .


2
이 게시물을 준비하는 과정에서 gcc에 버그 보고서를 제출하여 좀 더 안전하게 만들기 위해 경고를 추가 할 수 있기를 바랍니다. gcc.gnu.org/bugzilla/show_bug.cgi?id=54924
— David Stone

4

C ++ 14에서는 이제 리터럴을 사용할 수 있습니다.

using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3

1
및 제 2 라인은 교대로 더 잘로서, 이럴, 쓸 수있다auto s{"a\0b"s};
— underscore_d

좋은 답변 감사합니다.
— JOMA


1

anonym의 대답은 훌륭하지만 C ++ 98에는 비 매크로 솔루션도 있습니다.

template <size_t N>
std::string RawString(const char (&ch)[N])
{
  return std::string(ch, N-1);  // Again, exclude trailing `null`
}

이 함수를 사용하면 다음과 RawString(/* literal */)같은 문자열이 생성됩니다 S(/* literal */).

std::string my_string_t(RawString("a\0b"));
std::string my_string_m(S("a\0b"));
std::cout << "Using template: " << my_string_t << std::endl;
std::cout << "Using macro: " << my_string_m << std::endl;

또한 매크로에 문제가 있습니다. 표현식은 실제로 std::string작성된 것이 아니므 로 간단한 할당 초기화에 사용할 수 없습니다.

std::string s = S("a\0b"); // ERROR!

... 따라서 다음을 사용하는 것이 좋습니다.

#define std::string(s, sizeof s - 1)

프로젝트에서 하나 또는 다른 솔루션 만 사용하고 적절하다고 생각하는 이름으로 불러야합니다.


-5

나는이 질문이 오랫동안 제기되었다는 것을 알고 있습니다. 그러나 비슷한 문제가있는 사람이라면 다음 코드에 관심이있을 수 있습니다.

CComBSTR(20,"mystring1\0mystring2\0")

이 답변은 Microsoft 플랫폼에 너무 구체적이며 원래 질문 (std :: string에 대해 질문 함)을 다루지 않습니다.
— June Rhodes 2012

-8

std :: strings의 거의 모든 구현은 null로 종료되므로이 작업을 수행해서는 안됩니다. "a \ 0b"는 자동 널 종결 자 (a, 널, b, 널)로 인해 실제로 4 자 길이입니다. 정말로 이것을하고 std :: string의 계약을 깨고 싶다면 다음과 같이 할 수 있습니다.

std::string s("aab");
s.at(1) = '\0';

그러나 그렇게한다면 모든 친구들이 당신을 비웃을 것이고 진정한 행복을 찾을 수 없을 것입니다.


1
std :: string은 NULL로 종료 될 필요가 없습니다.
— Martin York

2
그럴 필요는 없지만 거의 모든 구현에서 이는 아마도 c_str () 접근자가 널 종료 등가물을 제공해야하기 때문일 것입니다.
— Jurney

2
효율성 을 위해 데이터 버퍼 뒷면에 널 문자를 유지할 수 있습니다. 그러나 문자열에 대한 어떤 연산 (즉, 메서드)도이 지식을 사용하지 않거나 NULL 문자를 포함하는 문자열의 영향을받지 않습니다. NULL 문자는 다른 문자와 똑같은 방식으로 조작됩니다.
— Martin York

이것이 문자열이 std ::라는 것이 너무 재밌는 이유입니다.-그 동작은 어떤 플랫폼에서도 정의되지 않았습니다.

나는 user595447이 여전히 여기에 있었으면 좋겠다. 그래서 지구상에서 그들이 말하고 있다고 생각한 것이 무엇인지 물어볼 수 있었다.
— underscore_d
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.