Você terá que ter cuidado com isso. Se você substituir 'b' por qualquer caractere numérico, você criará silenciosamente a string errada. Veja: stackoverflow.com/questions/10220401/…
David Stone
Respostas:
129
Desde C ++ 14
temos sido capazes de criar std::string
#include<iostream>#include<string>intmain(){
usingnamespacestd::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 do C ++ 14
O problema é o std::stringconstrutor que const char*assume que a entrada é uma string C. As strings C são \0encerradas e, portanto, a análise para quando atinge o \0caractere.
Para compensar isso, você precisa usar o construtor que constrói a string a partir de uma matriz char (não uma string C). Isso leva dois parâmetros - um ponteiro para a matriz e um comprimento:
std::stringx("pq\0rs"); // Two characters because input assumed to be C-Stringstd::stringx("pq\0rs",5); // 5 Characters as the input is now a char array with 5 characters.
Nota: C ++ NÃOstd::string é terminado (como sugerido em outras postagens). No entanto, você pode extrair um ponteiro para um buffer interno que contém uma C-String com o método . \0c_str()
atualização: a partir de c ++ 11, as strings têm terminação nula. Dito isso, a postagem de Loki continua válida.
matthewaveryusa
14
@mna: Eles têm terminação nula em termos de armazenamento, mas não no sentido de que são terminados em nulo com terminação nula significativa (ou seja, com semântica de definição de comprimento de string), que é o significado usual do termo.
Lightness Races in Orbit
Bem explicado. Obrigado.
Joma
22
Se você estiver fazendo a manipulação como faria com uma string de estilo C (matriz de caracteres), considere usar
std::vector<char>
Você tem mais liberdade para tratá-lo como um array da mesma maneira que trataria uma string C. Você pode usar copy () para copiar em uma string:
Se você está tentando codificar bytes em string (bytes grpc são armazenados como string), use o método vetorial conforme especificado na resposta; não da maneira usual (veja abaixo) que NÃO construirá a string inteira 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;
Alex Punnen
13
Não tenho ideia de por que você deseja fazer tal coisa, mas tente isto:
Quais são suas preocupações por fazer isso? Você está questionando a necessidade de armazenar "a \ 0b" sempre? ou questionando o uso de um std :: string para tal armazenamento? Nesse último caso, o que você sugere como alternativa?
Anthony Cramp,
3
@Constantin então você está fazendo algo errado se estiver armazenando dados binários como uma string. É para isso que vector<unsigned char>ou unsigned char *foram inventados.
Mahmoud Al-Qudsi
2
Eu me deparei com isso ao tentar aprender mais sobre segurança de strings. Eu queria testar meu código para ter certeza de que ele ainda funciona, mesmo que leia um caractere nulo durante a leitura de um arquivo / rede o que espera ser dados textuais. Eu uso std::stringpara indicar que os dados devem ser considerados como texto simples, mas estou fazendo um trabalho de hash e quero ter certeza de que tudo ainda funciona com caracteres nulos envolvidos. Isso parece um uso válido de um literal de string com um caractere nulo incorporado.
David Stone,
3
@DuckMaestro Não, isso não é verdade. Um \0byte em uma string UTF-8 só pode ser NUL. Um caractere codificado multibyte nunca conterá - \0nem qualquer outro caractere ASCII.
John Kugelman
1
Eu me deparei com isso ao tentar provocar um algoritmo em um caso de teste. Portanto, existem razões válidas; embora poucos.
Você terá que ter cuidado com isso. Se você substituir 'b' por qualquer caractere numérico, você criará silenciosamente a string errada usando a maioria dos métodos. Consulte: Regras para caracteres de escape de literais de string C ++ .
Por exemplo, deixei cair este trecho de aparência inocente no meio de um programa
// Create '\0' followed by '0' 40 times ;)std::stringstr("\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";
Essa foi minha primeira declaração impressa duas vezes, vários caracteres não impressos, seguidos por uma nova linha, seguida por algo na memória interna, que acabei de sobrescrever (e depois imprimi, mostrando que foi sobrescrito). Pior de tudo, mesmo compilar isso com avisos gcc completos e detalhados não me deu nenhuma indicação de que algo estava errado, e rodar o programa através do valgrind não reclamou de nenhum padrão de acesso à memória impróprio. Em outras palavras, é completamente indetectável por ferramentas modernas.
Você pode ter esse mesmo problema com o muito mais simples std::string("0", 100);, mas o exemplo acima é um pouco mais complicado e, portanto, mais difícil de ver o que está errado.
Felizmente, C ++ 11 nos dá uma boa solução para o problema usando a sintaxe da lista de inicializadores. Isso evita que você especifique o número de caracteres (o que, como mostrei acima, você pode fazer incorretamente) e evita combinar números de escape. std::string str({'a', '\0', 'b'})é seguro para qualquer conteúdo de string, ao contrário das versões que têm uma matriz de chare um tamanho.
Como parte da preparação para esta postagem, enviei um relatório de bug ao gcc na esperança de que eles adicionem um aviso para tornar isso um pouco mais seguro: gcc.gnu.org/bugzilla/show_bug.cgi?id=54924
David Stone
4
Em C ++ 14 agora você pode usar literais
usingnamespacestd::literals::string_literals;
std::string s = "a\0b"s;
std::cout << s.size(); // 3
Além disso, há um problema com a macro: a expressão não é realmente std::stringcomo escrita e, portanto, não pode ser usada, por exemplo, para inicialização de atribuição simples:
std::string s = S("a\0b"); // ERROR!
... então pode ser preferível usar:
#define std::string(s, sizeof s - 1)
Obviamente, você só deve usar uma ou outra solução em seu projeto e chamá-la do que achar apropriado.
Esta resposta é muito específica para plataformas Microsoft e não aborda a questão original (que era sobre std :: string).
Junho Rhodes
-8
Quase todas as implementações de std :: strings têm terminação nula, então você provavelmente não deveria fazer isso. Observe que "a \ 0b" tem na verdade quatro caracteres por causa do terminador nulo automático (a, nulo, b, nulo). Se você realmente deseja fazer isso e quebrar o contrato de std :: string, você pode fazer:
std::strings("aab");
s.at(1) = '\0';
mas se você fizer isso, todos os seus amigos vão rir de você, você nunca encontrará a verdadeira felicidade.
Não é obrigatório, mas em quase todas as implementações é, provavelmente devido à necessidade do acessador c_str () fornecer o equivalente terminado em nulo.
Jurney,
2
Para maior eficiência, um caractere nulo pode ser mantido na parte de trás do buffer de dados. Mas nenhuma das operações (ou seja, métodos) em uma string usa esse conhecimento ou é afetada por uma string contendo um caractere NULL. O caractere NULL será manipulado exatamente da mesma maneira que qualquer outro caractere.
Martin York,
É por isso que é tão engraçado que string seja std :: - seu comportamento não é definido em NENHUMA plataforma.
Gostaria que o usuário 595447 ainda estivesse aqui para que eu pudesse perguntar sobre o que eles achavam que estavam falando.
Respostas:
Desde C ++ 14
temos sido capazes de criar
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 do C ++ 14
O problema é o
std::string
construtor queconst char*
assume que a entrada é uma string C. As strings C são\0
encerradas e, portanto, a análise para quando atinge o\0
caractere.Para compensar isso, você precisa usar o construtor que constrói a string a partir de uma matriz char (não uma string C). Isso leva dois parâmetros - um ponteiro para a matriz e um comprimento:
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 ++ NÃO
std::string
é terminado (como sugerido em outras postagens). No entanto, você pode extrair um ponteiro para um buffer interno que contém uma C-String com o método .\0
c_str()
Verifique também a resposta de Doug T abaixo sobre o uso de a
vector<char>
.Verifique também o RiaD para uma solução C ++ 14.
fonte
Se você estiver fazendo a manipulação como faria com uma string de estilo C (matriz de caracteres), considere usar
std::vector<char>
Você tem mais liberdade para tratá-lo como um array da mesma maneira que trataria uma string C. Você pode usar copy () para copiar em uma string:
std::vector<char> vec(100) strncpy(&vec[0], "blah blah blah", 100); std::string vecAsStr( vec.begin(), vec.end());
e você pode usá-lo em muitos dos mesmos lugares onde você pode usar strings C
printf("%s" &vec[0]) vec[10] = '\0'; vec[11] = 'b';
Naturalmente, no entanto, você sofre dos mesmos problemas que as cordas C. Você pode esquecer seu terminal nulo ou escrever além do espaço alocado.
fonte
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;
Não tenho ideia de por que você deseja fazer tal coisa, mas tente isto:
std::string my_string("a\0b", 3);
fonte
vector<unsigned char>
ouunsigned char *
foram inventados.std::string
para indicar que os dados devem ser considerados como texto simples, mas estou fazendo um trabalho de hash e quero ter certeza de que tudo ainda funciona com caracteres nulos envolvidos. Isso parece um uso válido de um literal de string com um caractere nulo incorporado.\0
byte em uma string UTF-8 só pode ser NUL. Um caractere codificado multibyte nunca conterá -\0
nem qualquer outro caractere ASCII.Quais novos recursos os literais definidos pelo usuário adicionam ao C ++? apresenta uma resposta elegante: Defina
std::string operator "" _s(const char* str, size_t n) { return std::string(str, n); }
então você pode criar sua string desta forma:
std::string my_string("a\0b"_s);
ou mesmo assim:
auto my_string = "a\0b"_s;
Existe um jeito "antigo":
#define S(s) s, sizeof s - 1 // trailing NUL does not belong to the string
então você pode definir
std::string my_string(S("a\0b"));
fonte
O seguinte vai funcionar ...
std::string s; s.push_back('a'); s.push_back('\0'); s.push_back('b');
fonte
Você terá que ter cuidado com isso. Se você substituir 'b' por qualquer caractere numérico, você criará silenciosamente a string errada usando a maioria dos métodos. Consulte: Regras para caracteres de escape de literais de string C ++ .
Por exemplo, deixei cair este trecho de aparência inocente no meio de um 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";
Aqui está o que este programa produz para mim:
Entering loop. Entering loop. vector::_M_emplace_ba QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ
Essa foi minha primeira declaração impressa duas vezes, vários caracteres não impressos, seguidos por uma nova linha, seguida por algo na memória interna, que acabei de sobrescrever (e depois imprimi, mostrando que foi sobrescrito). Pior de tudo, mesmo compilar isso com avisos gcc completos e detalhados não me deu nenhuma indicação de que algo estava errado, e rodar o programa através do valgrind não reclamou de nenhum padrão de acesso à memória impróprio. Em outras palavras, é completamente indetectável por ferramentas modernas.
Você pode ter esse mesmo problema com o muito mais simples
std::string("0", 100);
, mas o exemplo acima é um pouco mais complicado e, portanto, mais difícil de ver o que está errado.Felizmente, C ++ 11 nos dá uma boa solução para o problema usando a sintaxe da lista de inicializadores. Isso evita que você especifique o número de caracteres (o que, como mostrei acima, você pode fazer incorretamente) e evita combinar números de escape.
std::string str({'a', '\0', 'b'})
é seguro para qualquer conteúdo de string, ao contrário das versões que têm uma matriz dechar
e um tamanho.fonte
Em C ++ 14 agora você pode usar literais
using namespace std::literals::string_literals; std::string s = "a\0b"s; std::cout << s.size(); // 3
fonte
auto s{"a\0b"s};
Melhor usar std :: vector <char> se esta questão não for apenas para fins educacionais.
fonte
A resposta do anônimo é excelente, mas também há uma solução não macro em C ++ 98:
template <size_t N> std::string RawString(const char (&ch)[N]) { return std::string(ch, N-1); // Again, exclude trailing `null` }
Com esta função,
RawString(/* literal */)
produzirá a mesma string queS(/* 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;
Além disso, há um problema com a macro: a expressão não é realmente
std::string
como escrita e, portanto, não pode ser usada, por exemplo, para inicialização de atribuição simples:std::string s = S("a\0b"); // ERROR!
... então pode ser preferível usar:
#define std::string(s, sizeof s - 1)
Obviamente, você só deve usar uma ou outra solução em seu projeto e chamá-la do que achar apropriado.
fonte
Eu sei que faz muito tempo que essa pergunta foi feita. Mas, para quem está tendo um problema semelhante, pode se interessar pelo código a seguir.
CComBSTR(20,"mystring1\0mystring2\0")
fonte
Quase todas as implementações de std :: strings têm terminação nula, então você provavelmente não deveria fazer isso. Observe que "a \ 0b" tem na verdade quatro caracteres por causa do terminador nulo automático (a, nulo, b, nulo). Se você realmente deseja fazer isso e quebrar o contrato de std :: string, você pode fazer:
std::string s("aab"); s.at(1) = '\0';
mas se você fizer isso, todos os seus amigos vão rir de você, você nunca encontrará a verdadeira felicidade.
fonte