Вы должны быть осторожны с этим. Если вы замените 'b' любым числовым символом, вы автоматически создадите неправильную строку. См .: stackoverflow.com/questions/10220401/…
Дэвид Стоун,
Ответы:
129
Начиная с C ++ 14
мы смогли создать буквальный std::string
#include<iostream>#include<string>intmain(){
usingnamespacestd::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-строка. C-строки \0завершаются, и поэтому анализ останавливается, когда достигает \0символа.
Чтобы компенсировать это, вам нужно использовать конструктор, который строит строку из массива символов (а не C-String). Он принимает два параметра - указатель на массив и длину:
std::stringx("pq\0rs"); // Two characters because input assumed to be C-Stringstd::stringx("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.
Примечание: C ++ std::stringэто НЕ\0 -завершённый (как это было предложено в других постах). Однако вы можете извлечь указатель на внутренний буфер, содержащий C-String с методом c_str().
Также ознакомьтесь с ответом Дуга Т ниже об использовании файла vector<char>.
обновление: начиная с C ++ 11 строки заканчиваются нулем. При этом пост Локи остается в силе.
matthewaveryusa
14
@mna: они заканчиваются нулем с точки зрения хранения, но не в том смысле, что они заканчиваются нулем со значимым завершением нулем (то есть с семантикой, определяющей длину строки), что является обычным значением этого термина.
Гонки легкости на орбите
Хорошо объяснено. Спасибо.
Joma
22
Если вы выполняете манипуляции, как со строкой в стиле c (массив символов), рассмотрите возможность использования
std::vector<char>
У вас есть больше свободы обращаться с ним как с массивом так же, как с c-строкой. Вы можете использовать copy () для копирования в строку:
Если вы говорите, что пытаетесь закодировать байты в строку (байты 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;
Alex Punnen
13
Понятия не имею, зачем вам это нужно, но попробуйте следующее:
Что вас беспокоит при этом? Вы сомневаетесь в необходимости хранить "a \ 0b" когда-либо? или сомневаетесь в использовании std :: string для такого хранения? Если последнее, что вы предлагаете в качестве альтернативы?
Энтони
3
@Constantin, то вы делаете что-то не так, если храните двоичные данные в виде строки. Для этого vector<unsigned char>или unsigned char *были придуманы.
Махмуд Аль-Кудси
2
Я столкнулся с этим, пытаясь узнать больше о безопасности строк. Я хотел протестировать свой код, чтобы убедиться, что он все еще работает, даже если он читает нулевой символ при чтении из файла / сети того, что он ожидает быть текстовыми данными. Я использую, std::stringчтобы указать, что данные следует рассматривать как простой текст, но я выполняю некоторую работу с хешированием и хочу убедиться, что все по-прежнему работает с задействованными нулевыми символами. Это похоже на допустимое использование строкового литерала со встроенным нулевым символом.
Дэвид Стоун
3
@DuckMaestro Нет, это неправда. \0Байт в строке UTF-8 может быть только NUL. \0Многобайтовый символ никогда не будет содержать - или любой другой символ ASCII в этом отношении.
Джон Кугельман
1
Я столкнулся с этим, когда пытался спровоцировать алгоритм в тестовом примере. Итак, есть веские причины; хотя и немного.
Вы должны использовать круглые скобки вместо квадратных скобок.
jk.
5
Вы должны быть осторожны с этим. Если вы замените 'b' любым числовым символом, вы автоматически создадите неправильную строку, используя большинство методов. См .: Правила для строковых литералов C ++ с escape-символом .
Например, я бросил этот невинно выглядящий фрагмент в середине программы.
// Create '\0' followed by '0' 40 times ;)std::stringstr("\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";
Это был мой первый оператор печати дважды, несколько непечатаемых символов, за которым следовала новая строка, а затем что-то во внутренней памяти, которое я просто перезаписал (а затем распечатал, показывая, что он был перезаписан). Хуже всего то, что даже компиляция этого с подробными и подробными предупреждениями gcc не дала мне никаких указаний на то, что что-то не так, а запуск программы через valgrind не жаловался на какие-либо неправильные шаблоны доступа к памяти. Другими словами, современные инструменты его совершенно не обнаруживают.
Вы можете получить ту же проблему с гораздо более простым std::string("0", 100);, но приведенный выше пример немного сложнее, и поэтому труднее понять, что не так.
К счастью, C ++ 11 дает нам хорошее решение проблемы с использованием синтаксиса списка инициализаторов. Это избавляет вас от необходимости указывать количество символов (что, как я показал выше, вы можете делать неправильно) и позволяет избежать объединения чисел с экранированием. std::string str({'a', '\0', 'b'})безопасен для любого строкового содержимого, в отличие от версий, которые принимают массив charи размер.
В рамках подготовки к этому посту я отправил в gcc отчет об ошибке в надежде, что они добавят предупреждение, чтобы сделать это немного безопаснее: gcc.gnu.org/bugzilla/show_bug.cgi?id=54924
Дэвид Стоун
4
В C ++ 14 теперь можно использовать литералы
usingnamespacestd::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
Кроме того, есть проблема с макросом: выражение на самом деле не так, std::stringкак написано, и поэтому не может использоваться, например, для простой инициализации присваивания:
std::string s = S("a\0b"); // ERROR!
... поэтому может быть предпочтительнее использовать:
#define std::string(s, sizeof s - 1)
Очевидно, вы должны использовать в своем проекте только то или иное решение и называть его так, как считаете нужным.
Этот ответ слишком специфичен для платформ Microsoft и не затрагивает исходный вопрос (который задан о std :: string).
Джун Родос,
-8
Почти все реализации std :: strings заканчиваются нулем, поэтому вам, вероятно, не стоит этого делать. Обратите внимание, что "a \ 0b" на самом деле состоит из четырех символов из-за автоматического нулевого терминатора (a, null, b, null). Если вы действительно хотите сделать это и разорвать контракт std :: string, вы можете сделать:
std::strings("aab");
s.at(1) = '\0';
но если вы это сделаете, все ваши друзья будут смеяться над вами, вы никогда не найдете настоящего счастья.
Это не обязательно, но почти во всех реализациях это возможно из-за необходимости в методе доступа c_str () предоставить вам эквивалент с завершающим нулем.
Jurney 02
2
Для эффективности на задней стороне буфера данных может храниться нулевой символ . Но ни одна из операций (т. Е. Методов) над строкой не использует это знание и на нее не влияет строка, содержащая символ NULL. Символ NULL будет обрабатываться точно так же, как и любой другой символ.
Мартин Йорк
Вот почему так забавно, что строка std :: - ее поведение не определено на ЛЮБОЙ платформе.
Я бы хотел, чтобы user595447 все еще был здесь, чтобы я мог спросить их, о чем они думают, о чем они говорят.
Ответы:
Начиная с 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
Проблема в том, что
std::string
конструктор, который принимает,const char*
предполагает, что ввод - это C-строка. C-строки\0
завершаются, и поэтому анализ останавливается, когда достигает\0
символа.Чтобы компенсировать это, вам нужно использовать конструктор, который строит строку из массива символов (а не 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
это НЕ\0
-завершённый (как это было предложено в других постах). Однако вы можете извлечь указатель на внутренний буфер, содержащий C-String с методомc_str()
.Также ознакомьтесь с ответом Дуга Т ниже об использовании файла
vector<char>
.Также проверьте RiaD для решения C ++ 14.
источник
Если вы выполняете манипуляции, как со строкой в стиле 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-струны. Вы можете забыть свой нулевой терминал или писать за выделенное пространство.
источник
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;
Понятия не имею, зачем вам это нужно, но попробуйте следующее:
std::string my_string("a\0b", 3);
источник
vector<unsigned char>
илиunsigned char *
были придуманы.std::string
чтобы указать, что данные следует рассматривать как простой текст, но я выполняю некоторую работу с хешированием и хочу убедиться, что все по-прежнему работает с задействованными нулевыми символами. Это похоже на допустимое использование строкового литерала со встроенным нулевым символом.\0
Байт в строке UTF-8 может быть только NUL.\0
Многобайтовый символ никогда не будет содержать - или любой другой символ ASCII в этом отношении.Какие новые возможности добавляют в 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 ++ с 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);
, но приведенный выше пример немного сложнее, и поэтому труднее понять, что не так.К счастью, 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
источник
auto s{"a\0b"s};
Лучше использовать std :: vector <char>, если этот вопрос не только в образовательных целях.
источник
ответ анонима отличный, но в 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)
Очевидно, вы должны использовать в своем проекте только то или иное решение и называть его так, как считаете нужным.
источник
Я знаю, что этот вопрос задают давно. Но для тех, кто сталкивается с подобной проблемой, может быть интересен следующий код.
CComBSTR(20,"mystring1\0mystring2\0")
источник
Почти все реализации std :: strings заканчиваются нулем, поэтому вам, вероятно, не стоит этого делать. Обратите внимание, что "a \ 0b" на самом деле состоит из четырех символов из-за автоматического нулевого терминатора (a, null, b, null). Если вы действительно хотите сделать это и разорвать контракт std :: string, вы можете сделать:
std::string s("aab"); s.at(1) = '\0';
но если вы это сделаете, все ваши друзья будут смеяться над вами, вы никогда не найдете настоящего счастья.
источник