vacia - string library c++
¿Cómo se construye una std:: string con un null incrustado? (11)
Si quiero construir una 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 correcta?
Desde C ++ 14
hemos sido capaces de 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";
}
Antes de C ++ 14
El problema es que el constructor std::string
que toma un const char*
asume que la entrada es una cadena C. Las cadenas C terminan /0
y, por lo tanto, el análisis se detiene cuando alcanza el carácter /0
.
Para compensar esto, necesita usar el constructor que construye la cadena a partir de una matriz char (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 ++ std::string
NO está /0
-terminado (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 c_str()
.
También consulte la respuesta de Doug T a continuación sobre el uso de un vector<char>
.
También echa un vistazo a RiaD para una solución C ++ 14.
Casi todas las implementaciones de std :: strings tienen terminación nula, por lo que probablemente no deberías 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 quieres hacer esto y romper el contrato de std :: string, puedes 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.
En C ++ 14 ahora puede usar literales
using namespace std::literals::string_literals;
std::string s = "a/0b"s;
std::cout << s.size(); // 3
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 una 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 podría ser preferible usar:
#define std::string(s, sizeof s - 1)
Obviamente, solo deberías usar una u otra solución en tu proyecto y llamarla como lo consideres apropiado.
Lo siguiente funcionará ...
std::string s;
s.push_back(''a'');
s.push_back(''/0'');
s.push_back(''b'');
No tengo idea de por qué querrías hacer tal cosa, pero intenta esto:
std::string my_string("a/0b", 3);
Sé que hace mucho tiempo que se ha formulado esta pregunta. Pero para cualquiera que tenga un problema similar podría interesarle el siguiente código.
CComBSTR(20,"mystring1/0mystring2/0")
Si está haciendo la manipulación como lo haría con una cadena de estilo c (matriz de caracteres), considere usar
std::vector<char>
Tienes más libertad para tratarlo como una matriz de la misma manera que tratarías una cadena. 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'';
Naturalmente, sin embargo, usted sufre los mismos problemas que c-strings. Puede olvidar su terminal nulo o escribir más allá del espacio asignado.
Tendrás que tener cuidado con esto. Si reemplaza ''b'' con cualquier carácter numérico, creará silenciosamente la cadena incorrecta usando la mayoría de los métodos. Ver: Reglas para caracteres de escape de literales C ++ .
Por ejemplo, dejé caer este fragmento de aspecto inocente en el 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 este programa me dio:
Entering loop.
Entering loop.
vector::_M_emplace_ba
QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
Esa fue mi primera declaración impresa en dos ocasiones, varios caracteres no impresos, seguidos de una nueva línea, seguida de algo en la memoria interna, que acabo de sobreescribir (y luego imprimí, mostrando que ha sido sobrescrito). Lo peor de todo es que incluso compilar esto con exhaustivas y detalladas advertencias de gcc no me dio ninguna indicación de que algo andara mal, y al ejecutar el programa a través de valgrind no me quejé de ningún patrón de acceso a memoria incorrecto. En otras palabras, es completamente indetectable con las herramientas modernas.
Puede obtener este mismo problema con std::string("0", 100);
mucho más simple std::string("0", 100);
, pero el ejemplo anterior es un poco más complicado, y por lo tanto es más difícil ver lo que está mal.
Afortunadamente, C ++ 11 nos brinda una buena solución al problema utilizando la sintaxis de la lista de inicializadores. Esto le ahorra tener que especificar el número de caracteres (que, como mostré anteriormente, puede hacer incorrectamente), y evita combinar los números que se han escapado. std::string str({''a'', ''/0'', ''b''})
es seguro para cualquier contenido de cadena, a diferencia de las versiones que toman una matriz de caracteres y un tamaño.
¿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 aún 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"));