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.