Si quiero construir un std :: string con una línea como:
std::string my_string("a\0b");
Donde quiero tener tres caracteres en la cadena resultante (a, nulo, b), solo obtengo uno. ¿Cuál es la sintaxis adecuada?
Si quiero construir un std :: string con una línea como:
std::string my_string("a\0b");
Donde quiero tener tres caracteres en la cadena resultante (a, nulo, b), solo obtengo uno. ¿Cuál es la sintaxis adecuada?
Respuestas:
hemos podido crear literal 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";
}
El problema es que el std::string
constructor que toma const char*
asume que la entrada es una C-string. Las cadenas C \0
terminan y, por lo tanto, el análisis se detiene cuando llega al \0
carácter.
Para compensar esto, necesita usar el constructor que construye la cadena a partir de una matriz de caracteres (no una C-String). Esto toma 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 ++ NOstd::string
está terminado (como se sugiere en otras publicaciones). Sin embargo, puede extraer un puntero a un búfer interno que contiene una C-String con el método . \0
c_str()
También consulte la respuesta de Doug T a continuación sobre el uso de a vector<char>
.
Consulte también RiaD para obtener una solución C ++ 14.
Si está haciendo 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 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 puedes usarlo en muchos de los mismos lugares donde puedes usar c-strings
printf("%s" &vec[0])
vec[10] = '\0';
vec[11] = 'b';
Sin embargo, naturalmente, sufre los mismos problemas que las cuerdas c. Puede olvidar su terminal nulo o escribir más allá del espacio asignado.
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;
No tengo idea de por qué querrías hacer tal cosa, pero prueba esto:
std::string my_string("a\0b", 3);
vector<unsigned char>
o unsigned char *
.
std::string
para indicar que los datos deben considerarse como texto sin formato, pero estoy haciendo un trabajo de hash y quiero asegurarme de que todo funcione con los caracteres nulos involucrados. Eso parece un uso válido de una cadena literal con un carácter nulo incrustado.
\0
byte en una cadena UTF-8 solo puede ser NUL. Un carácter codificado de varios bytes nunca contendrá, ni \0
ningún otro carácter ASCII para el caso.
¿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 aun así:
auto my_string = "a\0b"_s;
Hay una forma de "estilo antiguo":
#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. Consulte: 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 genera 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 línea nueva, seguida de algo en la memoria interna, que simplemente sobrescribí (y luego imprimí, mostrando que se ha sobrescrito). Lo peor de todo, incluso compilar esto con advertencias de gcc detalladas 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 incorrecto. En otras palabras, es completamente indetectable por las herramientas modernas.
Puede obtener este mismo problema con el 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 da una buena solución al problema usando la sintaxis de la lista de inicializadores. Esto le evita tener que especificar la cantidad de caracteres (que, como mostré anteriormente, puede hacerlo incorrectamente) y evita combinar números de escape. std::string str({'a', '\0', 'b'})
es seguro para cualquier contenido de cadena, a diferencia de las versiones que toman una variedad 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
auto s{"a\0b"s};
Es mejor usar std :: vector <char> si esta pregunta no es solo para fines educativos.
La respuesta de anonym 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 no es en realidad std::string
tal como está escrita y, por lo tanto, no se puede usar, por ejemplo, para una inicialización de asignación simple:
std::string s = S("a\0b"); // ERROR!
... por lo que sería preferible utilizar:
#define std::string(s, sizeof s - 1)
Obviamente, solo debe usar una u otra solución en su proyecto y llamarla como crea apropiado.
Sé que hace mucho que se hace 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 "a \ 0b" tiene en realidad cuatro caracteres debido al terminador nulo automático (a, nulo, b, nulo). Si realmente desea 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.