Как вы строите std :: string со встроенным нулем?

Если я хочу построить std :: string с линией вроде:

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

Где я хочу иметь три символа в результирующей строке (a, null, b), я получаю только один. Каков правильный синтаксис?

Поскольку C ++ 14

нам удалось создать литерал std::string

 #include  #include  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

Проблема заключается в конструкторе std::string который принимает const char* предполагает, что вход является C-строкой. С-строки заканчиваются на \0 и, таким образом, синтаксический анализ останавливается, когда он достигает символа \0 .

Чтобы компенсировать это, вам нужно использовать конструктор, который строит строку из массива char (а не C-String). Это принимает два параметра - указатель на массив и длину:

 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 NOT \0 -terminated (как указано в других сообщениях). Однако вы можете извлечь указатель на внутренний буфер, содержащий C-String, с помощью метода c_str() .

Также проверьте ниже ответ Doug T об использовании vector .

Также проверьте RiaD для решения C ++ 14.

Если вы делаете манипуляции, как в случае с строкой c-стиля (массив символов), используйте

 std::vector 

У вас больше свободы рассматривать его как массив таким же образом, как и для c-строки. Вы можете использовать copy () для копирования в строку:

 std::vector 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-строки. Вы можете забыть свой пустой терминал или зайти за выделенное пространство.

Я понятия не имею, почему вы хотите это сделать, но попробуйте это:

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

Какие новые возможности добавляют пользовательские литералы к 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")); 

Следующее будет работать …

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

Вы должны быть осторожны с этим. Если вы замените «b» на любой числовой символ, вы будете автоматически создавать неправильную строку, используя большинство методов. См .: Правила для символов строки строки C ++ .

Например, я бросил этот невинно выглядящий fragment в середине программы

 // 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 и размер.

В C ++ 14 теперь вы можете использовать литералы

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

Лучше использовать std :: vector , если этот вопрос предназначен не только для образовательных целей.

Ответ на аноним отличный, но в C ++ 98 есть немакро-решение:

 template  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) 

Очевидно, что вы должны использовать только одно или другое решение в своем проекте и называть его тем, что считаете нужным.

Я знаю, что этот вопрос давно задан. Но для тех, кто имеет подобную проблему, может быть интересен следующий код.

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

Почти во всех реализациях std :: string завершается нуль, поэтому вы, вероятно, не должны этого делать. Обратите внимание, что «a \ 0b» на самом деле состоит из четырех символов из-за автоматического нулевого терминатора (a, null, b, null). Если вы действительно хотите это сделать и разорвать контракт std :: string, вы можете сделать:

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

но если вы это сделаете, все ваши друзья будут смеяться над вами, вы никогда не найдете настоящего счастья.

  • Альтернатива itoa () для преобразования целого в строку C ++?
  • Каков наилучший способ обрезать std :: string?
  • std :: string :: c_str () и временные
  • Является ли std :: string refcounted в GCC 4.x / C ++ 11?
  • Право на перезаписывание std :: string null terminator?
  • Interesting Posts

    Как изменить имя экрана по умолчанию в OS X Lion?

    Хорошие дистрибутивы Linux для новичков

    Spring boot @ResponseBody не сериализует идентификатор объекта

    Почему я получаю «Исключение; должен быть пойман или объявлен брошенным “, когда я пытаюсь скомпилировать свой Java-код?

    Масштабировать изображение, чтобы заполнить ширину ImageView и сохранить пропорции

    Как я могу «отключить» пространство имен?

    Сделать node.js не выходить с ошибкой

    Автоматическое изменение размера элемента SELECT в соответствии с выбранной шириной OPTION

    Изменение расширения файла с помощью C #

    Есть ли в Firefox плагин проверки HTML5?

    Создание комбинаций Ctrl + Alt после переключения ключей с помощью Autohotkey

    как получить доступ к панели «Сеть» на инструментах разработчика Google Chrome с seleniumом?

    Параллельность Java: CAS vs Locking

    Когда я не должен реализовывать черту для ссылок на разработчиков этой черты?

    Как создать горячие клавиши для слов и предложений

    Давайте будем гением компьютера.