Pregunta

Si quiero construir un std::string con una línea como:

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

Cuando quiero tener tres caracteres en la cadena resultante (a, nulo, b), solo obtengo uno.¿Cuál es la sintaxis adecuada?

¿Fue útil?

Solución

Desde C ++ 14

hemos podido crear std::string

literales
#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";
}

Antes de C ++ 14

El problema es el constructor const char* que toma un \0 asume que la entrada es una C-cadena. Las cadenas C se terminan c_str() y, por lo tanto, el análisis se detiene cuando alcanza el carácter vector<char>.

Para compensar esto, debe usar el constructor que construye la cadena a partir de una matriz de caracteres (no una cadena C). Esto requiere dos parámetros: un puntero a la matriz y una longitud:

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.

Nota: C ++ <=> es NO <=> - finalizado (como se sugiere en otras publicaciones). Sin embargo, puede extraer un puntero a un búfer interno que contenga una C-String con el método <=>.

Consulte también la respuesta de Doug T a continuación sobre el uso de un <=>.

Consulte también RiaD para obtener una solución C ++ 14.

Otros consejos

Si está haciendo la manipulación como lo haría con una cadena de estilo c (matriz de caracteres) considere usar

std::vector<char>

Tiene más libertad para tratarlo como una matriz de la misma manera que trataría una cadena en C. Puede usar copy () para copiar en una cadena:

std::vector<char> vec(100)
strncpy(&vec[0], "blah blah blah", 100);
std::string vecAsStr( vec.begin(), vec.end());

y puede usarlo en muchos de los mismos lugares donde puede usar cadenas c

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

Naturalmente, sin embargo, sufre los mismos problemas que las cadenas c. Puede olvidar su terminal nula o escribir más allá del espacio asignado.

No tengo idea por qué querrías hacer algo así, pero prueba esto:

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

¿Qué nuevas capacidades agregan los literales definidos por el usuario a C ++? presenta una respuesta elegante: Definir

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

entonces puedes crear tu cadena de esta manera:

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

o incluso así:

auto my_string = "a\0b"_s;

Hay un " estilo antiguo " manera:

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

entonces puedes definir

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

Lo siguiente funcionará ...

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

Tendrás que tener cuidado con esto.Si reemplaza 'b' con cualquier carácter numérico, creará silenciosamente la cadena incorrecta utilizando la mayoría de los métodos.Ver: Reglas para caracteres de escape de literales de cadena de C++.

Por ejemplo, dejé caer este fragmento de aspecto inocente en medio de un programa.

// 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";

Esto es lo que me produjo este programa:

Entering loop.
Entering loop.

vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ

Esa fue mi primera declaración impresa dos veces, varios caracteres que no se imprimen, seguidos de una nueva línea, seguido de algo en la memoria interna, que simplemente sobrescribí (y luego imprimí, mostrando que se sobrescribió).Lo peor de todo es que incluso compilar esto con advertencias gcc exhaustivas y detalladas no me dio ninguna indicación de que algo estuviera mal, y ejecutar el programa a través de valgrind no se quejó de ningún patrón de acceso a la memoria inadecuado.En otras palabras, es completamente indetectable con las herramientas modernas.

Puedes tener este mismo problema con el método mucho más simple. std::string("0", 100);, pero el ejemplo anterior es un poco más complicado y, por lo tanto, más difícil de ver qué está mal.

Afortunadamente, C++ 11 nos brinda una buena solución al problema utilizando la sintaxis de la lista de inicializadores.Esto le evita tener que especificar la cantidad de caracteres (lo cual, como mostré anteriormente, puede hacerlo incorrectamente) y evita combinar números escapados. std::string str({'a', '\0', 'b'}) es seguro para cualquier contenido de cadena, a diferencia de las versiones que toman una variedad de char y un tamaño.

En C ++ 14 ahora puede usar literales

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

Mejor usar std :: vector < char > si esta pregunta no es solo para fines educativos.

La respuesta del anónimo es excelente, pero también hay una solución no macro en C ++ 98:

template <size_t N>
std::string RawString(const char (&ch)[N])
{
  return std::string(ch, N-1);  // Again, exclude trailing `null`
}

Con esta función, RawString(/* literal */) producirá la misma cadena que 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;

Además, hay un problema con la macro: la expresión en realidad no es un std::string como está escrita y, por lo tanto, no se puede usar, p. para inicialización de asignación simple:

std::string s = S("a\0b"); // ERROR!

... por lo que podría ser preferible usar:

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

Obviamente, solo debe usar una u otra solución en su proyecto y llamarlo como considere apropiado.

Sé que hace mucho tiempo que se ha hecho esta pregunta. Pero para cualquiera que tenga un problema similar podría estar interesado en el siguiente código.

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

Casi todas las implementaciones de std :: strings tienen terminación nula, por lo que probablemente no debería hacer esto. Tenga en cuenta que & Quot; a \ 0b & Quot; en realidad tiene cuatro caracteres debido al terminador nulo automático (a, null, b, null). Si realmente quiere hacer esto y romper el contrato de std :: string, puede hacer:

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

pero si lo haces, todos tus amigos se reirán de ti, nunca encontrarás la verdadera felicidad.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top