Якщо я хочу побудувати std :: рядок з рядком, як:
std::string my_string("a\0b");
Там, де я хочу мати три символи в отриманому рядку (a, null, b), я отримую лише один. Який правильний синтаксис?
Якщо я хочу побудувати std :: рядок з рядком, як:
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-рядок. С-рядки \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
це НЕ \0
-завершённий (як це було запропоновано в інших постах). Однак ви можете витягнути покажчик на внутрішній буфер, який містить C-String разом із методом c_str()
.
Також ознайомтеся з відповіддю Дага Т про використання avector<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 ++ символу екранування .
Наприклад, я скинув цей невинно виглядаючий фрагмент посеред програми
// 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';
але якщо ви це зробите, всі ваші друзі будуть сміятися з вас, ви ніколи не знайдете справжнього щастя.