Se eu quiser construir um std :: string com uma linha como:
std::string my_string("a\0b");
Onde eu quero ter três caracteres na string resultante (a, null, b), eu só consigo um. Qual é a sintaxe adequada?
Se eu quiser construir um std :: string com uma linha como:
std::string my_string("a\0b");
Onde eu quero ter três caracteres na string resultante (a, null, b), eu só consigo um. Qual é a sintaxe adequada?
Respostas:
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";
}
O problema é o std::string
construtor que const 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Ã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 . \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.
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.
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);
vector<unsigned char>
ou unsigned 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"));
O seguinte vai funcionar ...
std::string s;
s.push_back('a');
s.push_back('\0');
s.push_back('b');
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 de char
e um tamanho.
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
auto s{"a\0b"s};
Melhor usar std :: vector <char> se esta questão não for apenas para fins educacionais.
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 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;
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.
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")
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.