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


Если я хочу построить std:: string со строкой типа:

std::string my_string("ab");

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

11 72

11 ответов:

С C++14

мы смогли создать литерал std::string

#include <iostream>
#include <string>

int main()
{
    using namespace std::string_literals;

    std::string s = "pl--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-String). Это занимает два параметры - указатель на массив и его длину:

std::string   x("pqrs");   // Two characters because input assumed to be C-String
std::string   x("pqrs",5); // 5 Characters as the input is now a char array with 5 characters.

Примечание: C++ std::string и не-прекращено (как предложено в других постах). Однако вы можете извлечь указатель на внутренний буфер, содержащий C-строку с помощью метода 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-strings

printf("%s" &vec[0])
vec[10] = '';
vec[11] = 'b';

естественно, однако вы страдаете от тех же проблем, что и C-стринги. Вы можете забыть свой нулевой терминал или написать мимо выделенного пространства.

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

std::string my_string("ab", 3);

какие новые возможности добавляют пользовательские литералы в C++? представляет элегантный ответ: определить

std::string operator "" _s(const char* str, size_t n) 
{ 
    return std::string(str, n); 
}

тогда вы можете создать свою строку следующим образом:

std::string my_string("ab"_s);

или даже так:

auto my_string = "ab"_s;

есть способ "старого стиля":

#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string

затем вы можете определить

std::string my_string(S("ab"));

следующее будет работать...

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

вы должны быть осторожны с этим. Если вы замените 'b' любым числовым символом, вы молча создадите неправильную строку, используя большинство методов. Смотрите:правила для строковых литералов C++ escape-символ.

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

// Create '' followed by '0' 40 times ;)
std::string str("", 80);
std::cerr << "Entering loop.\n";
for (char & c : str) {
    std::cerr << c;
    // 'Q' is way cooler than '' 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', '', 'b'}) безопасно для любого строкового содержимого, в отличие от версий, которые принимают массив char и размер.

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

using namespace std::literals::string_literals;
std::string s = "ab"s;
std::cout << s.size(); // 3

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

ответ 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("ab"));
std::string my_string_m(S("ab"));
std::cout << "Using template: " << my_string_t << std::endl;
std::cout << "Using macro: " << my_string_m << std::endl;

кроме того, есть проблема с макросом: выражение не является на самом деле std::string как написано, и поэтому не может быть использован, например, для простого назначения-инициализации:

std::string s = S("ab"); // ERROR!

...поэтому было бы предпочтительнее использовать:

#define std::string(s, sizeof s - 1)

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

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

CComBSTR(20,"mystring1mystring2")

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

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