Если я хочу построить std::string с строкой вроде:
std::string my_string("a\0b");
Где я хочу иметь три символа в результирующей строке (a, null, b), я получаю только один. Каков правильный синтаксис?
Если я хочу построить std::string с строкой вроде:
std::string my_string("a\0b");
Где я хочу иметь три символа в результирующей строке (a, null, b), я получаю только один. Каков правильный синтаксис?
нам удалось создать литерал 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";
}
Проблема заключается в конструкторе std::string
, который принимает значение const char*
, полагает, что ввод является C-строкой. 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.
Примечание. С++ std::string
НЕ \0
-terminated (как указано в других сообщениях). Однако вы можете извлечь указатель на внутренний буфер, содержащий C-String, с помощью метода c_str()
.
Также проверьте ответ Doug T ниже об использовании vector<char>
.
Также проверьте RiaD для решения на С++ 14.
Если вы делаете манипуляции, как в строках c-style (массив символов), используйте
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-строки. Вы можете забыть свой пустой терминал или записать за выделенное пространство.
Я понятия не имею, зачем вы хотите это сделать, но попробуйте следующее:
std::string my_string("a\0b", 3);
Какие новые возможности добавляют пользовательские литералы к С++? представляет собой элегантный ответ: Определить
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" на любой числовой символ, вы будете автоматически создавать неправильную строку, используя большинство методов. См. Правила для символа escape-символов строк С++.
Например, я сбросил этот невинно выглядящий фрагмент в середине программы
// 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);
, но приведенный выше пример немного сложнее и, следовательно, сложнее увидеть, что не так.
К счастью, С++ 11 дает нам хорошее решение проблемы с использованием синтаксиса списка инициализаторов. Это избавит вас от необходимости указывать количество символов (что, как я показал выше, вы можете сделать неправильно) и избегает комбинирования экранированных номеров. std::string str({'a', '\0', 'b'})
безопасен для любого содержимого строки, в отличие от версий, которые принимают массив char
и размер.
В С++ 14 теперь вы можете использовать литералы
using namespace std::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
Лучше использовать std::vector <char> если этот вопрос не только для образовательных целей.
анонимный ответ превосходный, но в С++ 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)
Очевидно, что вы должны использовать только одно или другое решение в своем проекте и называть его тем, что считаете нужным.
Я знаю, что этот вопрос давно задан. Но для тех, кто имеет подобную проблему, может быть интересен следующий код.
CComBSTR(20,"mystring1\0mystring2\0")
Почти все реализации std:: строк завершены с нулевой отметкой, поэтому вы, вероятно, не должны этого делать. Обратите внимание, что "a\0b" на самом деле состоит из четырех символов из-за автоматического нулевого терминатора (a, null, b, null). Если вы действительно хотите это сделать и разорвать контракт std::string, вы можете сделать:
std::string s("aab");
s.at(1) = '\0';
но если вы это сделаете, все ваши друзья будут смеяться над вами, вы никогда не найдете настоящего счастья.