Não consigo entender as diferenças entre std::string
e std::wstring
. Eu sei que wstring
suporta caracteres largos, como caracteres Unicode. Eu tenho as seguintes perguntas:
- Quando devo usar
std::wstring
maisstd::string
? - Pode
std::string
conter todo o conjunto de caracteres ASCII, incluindo os caracteres especiais? - É
std::wstring
suportado por todos os compiladores C ++ populares? - O que é exatamente um " caractere amplo "?
Respostas:
string
?wstring
?std::string
é umbasic_string
modelo em umchar
estd::wstring
em umwchar_t
.char
vs.wchar_t
char
deve conter um caractere, geralmente um caractere de 8 bits.wchar_t
deve conter um caractere amplo e, em seguida, as coisas ficam complicadas:no Linux, a
wchar_t
é de 4 bytes, enquanto no Windows, é de 2 bytes.E o Unicode , então?
O problema é que nem
char
nemwchar_t
está diretamente vinculado ao unicode.No Linux?
Vamos usar um sistema operacional Linux: meu sistema Ubuntu já reconhece unicode. Quando trabalho com uma sequência de caracteres, ela é nativamente codificada em UTF-8 (isto é, sequência de caracteres Unicode). O código a seguir:
gera o seguinte texto:
Você verá que o texto "olé"
char
é realmente construído por quatro caracteres: 110, 108, 195 e 169 (sem contar o zero à direita). (Eu vou deixar você estudar owchar_t
código como um exercício)Portanto, ao trabalhar com um
char
no Linux, você geralmente deve usar o Unicode sem nem mesmo saber. E, comostd::string
funcionachar
,std::string
já está pronto para unicode.Observe que
std::string
, como a API da string C, considerará a string "olé" com 4 caracteres, não três. Portanto, você deve ter cuidado ao truncar / jogar com caracteres unicode, porque alguma combinação de caracteres é proibida no UTF-8.No Windows?
No Windows, isso é um pouco diferente. O Win32 precisava oferecer suporte a vários aplicativos que trabalhavam com
char
e em diferentes charsets / páginas de código produzidas em todo o mundo, antes do advento do Unicode.Portanto, a solução foi interessante: se um aplicativo funciona
char
, as seqüências de caracteres são codificadas / impressas / exibidas nas etiquetas da GUI usando o charset / página de código local na máquina. Por exemplo, "olé" seria "olé" em um Windows localizado em francês, mas seria algo diferente em um Windows localizado em cirílico ("olé" se você usar o Windows-1251 ). Assim, "aplicativos históricos" geralmente ainda funcionam da mesma maneira antiga.Para aplicativos baseados em Unicode, o Windows usa
wchar_t
2 bytes de largura e é codificado em UTF-16 , que é codificado em Unicode em caracteres de 2 bytes (ou, no mínimo, o UCS-2 mais compatível, que é quase o mesma coisa IIRC).Os aplicativos que usam
char
são denominados "multibyte" (porque cada glifo é composto de um ou maischar
s), enquanto os aplicativos que usamwchar_t
são denominados "widechar" (porque cada glifo é composto de um ou dois)wchar_t
. Consulte a API de conversão MultiByteToWideChar e WideCharToMultiByte Win32 para obter mais informações.Portanto, se você trabalha no Windows, deseja muito usá-lo
wchar_t
(a menos que use uma estrutura oculta, como GTK + ou QT ...). O fato é que, nos bastidores, o Windows trabalha comwchar_t
cadeias de caracteres, portanto, mesmo aplicativos históricos terão suaschar
cadeias de caracteres convertidaswchar_t
ao usar API comoSetWindowText()
(função de API de baixo nível para definir o rótulo em uma GUI do Win32).Problemas de memória?
UTF-32 tem 4 bytes por caractere, portanto, não há muito a acrescentar, se apenas um texto UTF-8 e UTF-16 sempre usarem menos ou a mesma quantidade de memória que um texto UTF-32 (e geralmente menos )
Se houver um problema de memória, você deve saber que, para a maioria dos idiomas ocidentais, o texto UTF-8 utilizará menos memória que o mesmo UTF-16.
Ainda assim, para outros idiomas (chinês, japonês etc.), a memória usada será a mesma ou um pouco maior para UTF-8 do que para UTF-16.
Em suma, o UTF-16 geralmente usa 2 e ocasionalmente 4 bytes por caracteres (a menos que você esteja lidando com algum tipo de glifo de linguagem esotérica (Klingon? Elvish?), Enquanto o UTF-8 gasta de 1 a 4 bytes.
Veja http://en.wikipedia.org/wiki/UTF-8#Compared_to_UTF-16 para obter mais informações.
Conclusão
Quando devo usar std :: wstring sobre std :: string?
No Linux? Quase nunca (§).
No Windows? Quase sempre (§).
No código de plataforma cruzada? Depende do seu kit de ferramentas ...
(§): a menos que você use um conjunto de ferramentas / estrutura que diga o contrário
Pode
std::string
conter todo o conjunto de caracteres ASCII, incluindo caracteres especiais?Aviso: A
std::string
é adequado para armazenar um buffer 'binário', onde umstd::wstring
não é!No Linux? Sim.
No Windows? Somente caracteres especiais disponíveis para o código de idioma atual do usuário do Windows.
Editar (Após um comentário de Johann Gerell ):
a
std::string
será suficiente para manipularchar
cadeias baseadas em todas (cadachar
uma sendo um número de 0 a 255). Mas:char
NÃO são ASCII.char
de 0 a 127 será mantido corretamentechar
de 128 a 255 terá uma significação dependendo da sua codificação (unicode, não unicode etc.), mas poderá armazenar todos os glifos Unicode desde que sejam codificados em UTF-8.É
std::wstring
suportado por quase todos os compiladores C ++ populares?Principalmente, com exceção dos compiladores baseados em GCC que são portados para o Windows.
Ele funciona no meu g ++ 4.3.2 (no Linux) e usei a API Unicode no Win32 desde o Visual C ++ 6.
O que é exatamente um personagem amplo?
No C / C ++, é um tipo de caractere escrito
wchar_t
que é maior que ochar
tipo de caractere simples . Ele deve ser usado para inserir caracteres cujos índices (como glifos Unicode) são maiores que 255 (ou 127, dependendo ...).fonte
olè
é codificado como UTF-8 ou não. Ainda mais, a razão que você não pode nativamente transmitirwchar_t *
aosstd::cout
ocorre porque os tipos são incompatíveis resultando em um programa mal-formada e não tem nada a ver com o uso de codificações. Vale ressaltar que, se você usastd::string
oustd::wstring
depende da sua preferência de codificação e não da plataforma, especialmente se deseja que seu código seja portátil.std::wstring
deve ser usada no Windows porque é mais adequada à API do Windows Unicode, que eu acho falaciosa. Se sua única preocupação era chamar a API do Windows Unicode e não organizar as seqüências de caracteres, com certeza, mas eu não compro isso como o caso geral.If your only concern was calling into the Unicode Windows API and not marshalling strings then sure
Então, nós concordamos. Estou codificando em C ++, não JavaScript. Evitar o empacotamento inútil ou qualquer outro processamento potencialmente caro em tempo de execução, quando isso pode ser feito em tempo de compilação, é o cerne desse idioma. Codificar e usar o WinAPIstd::string
é apenas um desperdício injustificado de recursos de tempo de execução. Você acha falacioso, e tudo bem, pois é seu ponto de vista. O meu próprio é que não escreverei código com pessimização no Windows apenas porque parece melhor do lado do Linux.Eu recomendo evitar
std::wstring
no Windows ou em outro lugar, exceto quando exigido pela interface ou em qualquer lugar próximo das chamadas da API do Windows e respectivas conversões de codificação como um açúcar sintático.Minha visão está resumida em http://utf8everywhere.org, da qual sou coautora.
A menos que seu aplicativo seja centralizado em chamadas de API, por exemplo, principalmente aplicativos de interface do usuário, a sugestão é armazenar seqüências de caracteres Unicode em std :: string e codificadas em UTF-8, realizando conversões próximas a chamadas de API. Os benefícios descritos no artigo superam o aparente aborrecimento da conversão, especialmente em aplicações complexas. Isso é duplamente válido para o desenvolvimento de várias plataformas e bibliotecas.
E agora, respondendo suas perguntas:
fonte
Portanto, todo leitor aqui agora deve ter um entendimento claro sobre os fatos, a situação. Caso contrário, você deve ler a resposta excepcionalmente abrangente de paercebal [btw: obrigado!].
Minha conclusão pragmática é surpreendentemente simples: todo esse material de "codificação de caracteres" em C ++ (e STL) é substancialmente quebrado e inútil. Culpe a Microsoft ou não, isso não ajudará de qualquer maneira.
Minha solução, após uma investigação aprofundada, muita frustração e as consequentes experiências é a seguinte:
aceite, que você tem que ser responsável por si mesmo pelo material de codificação e conversão (e verá que muito disso é trivial)
use std :: string para qualquer string UTF-8 (apenas a
typedef std::string UTF8String
)aceite que esse objeto UTF8String é apenas um recipiente idiota, mas barato. Nunca acesse e / ou manipule caracteres diretamente (nunca procure, substitua etc.). Você poderia, mas realmente não quer perder seu tempo escrevendo algoritmos de manipulação de texto para seqüências de caracteres de vários bytes! Mesmo se outras pessoas já fizeram coisas tão estúpidas, não faça isso! Deixe ser! (Bem, há cenários em que faz sentido ... basta usar a biblioteca da UTI para esses).
use std :: wstring para cadeias codificadas UCS-2 (
typedef std::wstring UCS2String
) - isso é um compromisso e uma concessão à bagunça que a API do WIN32 introduziu). UCS-2 é suficiente para a maioria de nós (mais sobre isso mais tarde ...).use instâncias UCS2String sempre que um acesso caractere por caractere for necessário (leitura, manipulação e assim por diante). Qualquer processamento baseado em caracteres deve ser feito em uma representação NÃO multibyte. É simples, rápido, fácil.
adicione duas funções utilitárias para converter entre UTF-8 e UCS-2:
As conversões são diretas, o Google deve ajudar aqui ...
É isso aí. Use UTF8String sempre que a memória for preciosa e para todas as E / S UTF-8. Use UCS2String sempre que a sequência deve ser analisada e / ou manipulada. Você pode converter entre essas duas representações a qualquer momento.
Alternativas e melhorias
as conversões de & para codificações de caracteres de byte único (por exemplo, ISO-8859-1) podem ser realizadas com a ajuda de tabelas de conversão simples, por exemplo,
const wchar_t tt_iso88951[256] = {0,1,2,...};
e o código apropriado para a conversão de & para UCS2.se UCS-2 não for suficiente, mude para UCS-4 (
typedef std::basic_string<uint32_t> UCS2String
)UTI ou outras bibliotecas unicode?
Para coisas avançadas.
fonte
Quando você deseja ter caracteres largos armazenados em sua string.
wide
depende da implementação. O Visual C ++ é padronizado para 16 bits, se bem me lembro, enquanto o GCC é padronizado, dependendo do destino. Aqui tem 32 bits. Observe que wchar_t (tipo de caractere amplo) não tem nada a ver com unicode. É apenas garantido que ele pode armazenar todos os membros do maior conjunto de caracteres que a implementação suporta por seus códigos de idioma e pelo menos enquanto char. Você pode armazenar seqüências de caracteres unicode muito bemstd::string
usando não fornecerá a quantidade de caracteres lógicos em sua sequência, mas apenas a quantidade de elementos char ou wchar_t armazenados nessa string / wstring. Por esse motivo, o pessoal do wrapper gtk / glib C ++ desenvolveu uma classe que pode lidar com utf-8.utf-8
codificação também. Mas não entenderá o significado dos pontos de código unicode. assimstr.size()
Glib::ustring
Se o seu wchar_t tiver 32 bits, você poderá usá-lo
utf-32
como uma codificação unicode e poderá armazenar e manipular cadeias unicode usando uma codificação fixa (utf-32 é comprimento fixo). Isto significa do seu wstrings.size()
função irá , em seguida, retornar a quantidade certa de elementos wchar_t e caracteres lógicos.fonte
std::wstring
.Eu freqüentemente uso std :: string para armazenar caracteres utf-8 sem nenhum problema. Eu recomendo sinceramente fazer isso ao fazer interface com APIs que usam utf-8 como o tipo de string nativo também.
Por exemplo, eu uso utf-8 ao fazer interface com meu código com o interpretador Tcl.
A principal ressalva é o comprimento da string std ::, não é mais o número de caracteres na string.
fonte
fonte
fonte
Os aplicativos que não estão satisfeitos com apenas 256 caracteres diferentes têm a opção de usar caracteres largos (mais de 8 bits) ou uma codificação de comprimento variável (uma codificação multibyte na terminologia C ++), como UTF-8. Caracteres largos geralmente exigem mais espaço do que uma codificação de tamanho variável, mas são mais rápidos de processar. Os aplicativos multilíngues que processam grandes quantidades de texto geralmente usam caracteres largos ao processar o texto, mas o convertem em UTF-8 ao armazená-lo em disco.
A única diferença entre a
string
e awstring
é o tipo de dados dos caracteres que eles armazenam. Uma string armazenachar
s cujo tamanho é garantido como pelo menos 8 bits, para que você possa usar strings para processamento, por exemplo, texto ASCII, ISO-8859-15 ou UTF-8. O padrão não diz nada sobre o conjunto de caracteres ou codificação.Praticamente todo compilador usa um conjunto de caracteres cujos primeiros 128 caracteres correspondem ao ASCII. Este também é o caso de compiladores que usam codificação UTF-8. O importante a ter em atenção ao usar cadeias de caracteres em UTF-8 ou alguma outra codificação de tamanho variável, é que os índices e comprimentos são medidos em bytes, não em caracteres.
O tipo de dado de uma wstring é
wchar_t
, cujo tamanho não é definido no padrão, exceto que deve ser pelo menos tão grande quanto um caractere, geralmente 16 bits ou 32 bits. O wstring pode ser usado para processar texto na codificação de caracteres largos definida pela implementação. Como a codificação não está definida no padrão, não é fácil converter entre seqüências de caracteres e wstrings. Também não se pode presumir que os wstrings tenham uma codificação de comprimento fixo.Se você não precisar de suporte em vários idiomas, poderá usar apenas seqüências regulares. Por outro lado, se você estiver escrevendo um aplicativo gráfico, geralmente a API suporta apenas caracteres largos. Então você provavelmente deseja usar os mesmos caracteres largos ao processar o texto. Lembre-se de que UTF-16 é uma codificação de comprimento variável, o que significa que você não pode assumir o
length()
retorno do número de caracteres. Se a API usar uma codificação de comprimento fixo, como UCS-2, o processamento se tornará fácil. É difícil fazer a conversão entre caracteres largos e UTF-8 de maneira portátil, mas, novamente, a API da interface do usuário provavelmente suporta a conversão.fonte
Uma boa pergunta! Eu acho que a codificação de dados (às vezes um CHARSET também está envolvido) é um mecanismo de expressão de memória para salvar dados em um arquivo ou transferir dados por uma rede, por isso respondo a esta pergunta como:
1. Quando devo usar std :: wstring sobre std :: string?
Se a plataforma de programação ou a função API for de um byte e desejamos processar ou analisar alguns dados Unicode, por exemplo, ler arquivos do Windows '.REG ou fluxo de 2 bytes da rede, devemos declarar a variável std :: wstring com facilidade processá-los. por exemplo: wstring ws = L "中国 a" (memória de 6 octetos: 0x4E2D 0x56FD 0x0061), podemos usar ws [0] para obter o caractere '中' e ws [1] para obter o caractere '国' e ws [2] para obter o caractere 'a' etc.
2. O std :: string pode conter todo o conjunto de caracteres ASCII, incluindo os caracteres especiais?
Sim. Mas observe: ASCII americano, significa que cada octeto 0x00 ~ 0xFF representa um caractere, incluindo texto imprimível como "123abc & * _ &" e você disse que um especial, principalmente o imprime como '.' evite confundir editores ou terminais. E alguns outros países estendem seu próprio conjunto de caracteres "ASCII", por exemplo, chinês, usam 2 octetos para representar um caractere.
3. O std :: wstring é suportado por todos os compiladores C ++ populares?
Talvez, ou principalmente. Eu usei: VC ++ 6 e GCC 3.3, SIM
4. O que é exatamente um "caractere amplo"?
um caractere amplo indica principalmente o uso de 2 ou 4 octetos para conter os caracteres de todos os países. 2 octetos UCS2 é uma amostra representativa e, além disso, por exemplo, o inglês 'a', sua memória é 2 octetos de 0x0061 (vs na memória ASCII 'a é 1 octeto 0x61)
fonte
Há algumas respostas muito boas aqui, mas acho que há algumas coisas que posso adicionar em relação ao Windows / Visual Studio. Isso é baseado na minha experiência com o VS2015. No Linux, basicamente a resposta é usar UTF-8 codificado em
std::string
qualquer lugar. No Windows / VS, fica mais complexo. Aqui está o porquê. O Windows espera que as seqüências armazenadas usandochar
s sejam codificadas usando a página de código do código de idioma. Esse é quase sempre o conjunto de caracteres ASCII seguido por 128 outros caracteres especiais, dependendo da sua localização. Deixe-me apenas declarar que isso não apenas ao usar a API do Windows, há outros três locais importantes em que essas seqüências de caracteres interagem com o C ++ padrão. Estes são literais de string, com saída estd::cout
uso de<<
um nome de arquivo parastd::fstream
.Serei sincero aqui, pois sou um programador, não um especialista em idiomas. Aprecio que USC2 e UTF-16 não são os mesmos, mas para meus propósitos eles são próximos o suficiente para serem intercambiáveis e eu os uso como tal aqui. Na verdade, não tenho certeza de qual Windows usa, mas geralmente também não preciso saber. Eu afirmei o UCS2 nesta resposta; portanto, desculpe-me antecipadamente se incomodar alguém com minha ignorância sobre esse assunto e fico feliz em alterá-lo se houver alguma coisa errada.
Literais de string
Se você digitar literais de seqüência de caracteres que contêm apenas caracteres que podem ser representados pela sua página de código, o VS os armazenará em seu arquivo com 1 byte por codificação de caracteres com base na sua página de código. Observe que, se você alterar sua página de código ou fornecer sua fonte para outro desenvolvedor usando uma página de código diferente, acho que (mas não testei) que o personagem terminará diferente. Se você executar seu código em um computador usando uma página de código diferente, não tenho certeza se o caractere também será alterado.
Se você digitar qualquer literal de string que não possa ser representado pela sua página de código, o VS solicitará que você salve o arquivo como Unicode. O arquivo será codificado como UTF-8. Isso significa que todos os caracteres não ASCII (incluindo os que estão na sua página de códigos) serão representados por 2 ou mais bytes. Isso significa que, se você fornecer sua fonte para outra pessoa, a fonte terá a mesma aparência. No entanto, antes de passar a fonte para o compilador, o VS converte o texto codificado em UTF-8 em texto codificado em página de código e todos os caracteres ausentes na página de código são substituídos
?
.A única maneira de garantir a representação correta de um literal de cadeia de caracteres Unicode no VS é preceder o literal de cadeia de caracteres,
L
tornando-o uma literal de cadeia de caracteres ampla. Nesse caso, o VS converterá o texto codificado em UTF-8 do arquivo em UCS2. Você precisa passar essa literal de string para umstd::wstring
construtor ou convertê-la em utf-8 e colocá-la em astd::string
. Ou, se desejar, você pode usar as funções da API do Windows para codificá-lo usando sua página de código para colocá-lo em umstd::string
, mas também pode não ter usado uma literal de cadeia ampla.std :: cout
Ao enviar para o console usando,
<<
você só pode usarstd::string
, não,std::wstring
e o texto deve ser codificado usando sua página de código do código de idioma. Se você tiver umstd::wstring
, deverá convertê-lo usando uma das funções da API do Windows e qualquer caractere que não esteja na sua página de código será substituído por?
(talvez você possa alterar o caractere, não lembro).std :: nomes de arquivos fstream
O sistema operacional Windows usa UCS2 / UTF-16 para seus nomes de arquivos, portanto, qualquer que seja sua página de código, você pode ter arquivos com qualquer caractere Unicode. Mas isso significa que, para acessar ou criar arquivos com caracteres que não estão na sua página de código, você deve usar
std::wstring
. Não há outro caminho. Esta é uma extensão específica da Microsoftstd::fstream
que provavelmente não será compilada em outros sistemas. Se você usar std :: string, poderá utilizar apenas nomes de arquivos que incluem apenas caracteres na sua página de código.Suas opções
Se você está apenas trabalhando no Linux, provavelmente não chegou tão longe. Basta usar UTF-8 em
std::string
qualquer lugar.Se você está apenas trabalhando no Windows, use o UCS2 em
std::wstring
qualquer lugar. Alguns puristas podem dizer que usam UTF8 e depois convertem quando necessário, mas por que se preocupar com o aborrecimento.Se você é multiplataforma, é uma bagunça ser franco. Se você tentar usar o UTF-8 em qualquer lugar do Windows, precisará ter muito cuidado com os literais de seqüência de caracteres e com a saída no console. Você pode facilmente corromper suas cordas lá. Se você usa
std::wstring
qualquer lugar do Linux, pode não ter acesso à versão amplastd::fstream
, portanto é necessário fazer a conversão, mas não há risco de corrupção. Então, pessoalmente, acho que essa é uma opção melhor. Muitos discordariam, mas eu não estou sozinho - é o caminho percorrido pelo wxWidgets, por exemplo.Outra opção poderia ser typedef
unicodestring
comostd::string
no Linux estd::wstring
no Windows, e ter uma macro chamada UNI () que prefixa L no Windows e nada no Linux, depois o códigoficaria bem em qualquer plataforma, eu acho.
Respostas
Então, para responder às suas perguntas
1) Se você está programando para o Windows, o tempo todo, se for multiplataforma, talvez o tempo todo, a menos que você queira lidar com possíveis problemas de corrupção no Windows ou escreva algum código com a plataforma específica
#ifdefs
para solucionar as diferenças, apenas usando Linux então nunca.2) sim Além do Linux, você também pode usá-lo para todos os Unicode. No Windows, você pode usá-lo apenas para todos os unicode se optar por codificar manualmente usando UTF-8. Mas a API do Windows e as classes C ++ padrão esperam que elas
std::string
sejam codificadas usando a página de código do código de idioma. Isso inclui todos os ASCII, além de outros 128 caracteres, que variam dependendo da página de código que o computador está configurado para usar.3) Eu acredito que sim, mas se não, então é apenas um simples typedef de um 'std :: basic_string' usando em
wchar_t
vez dechar
4) Um caractere amplo é um tipo de caractere maior que o
char
tipo padrão de 1 byte . No Windows, são 2 bytes, no Linux, são 4 bytes.fonte
/utf-8
).1) Como mencionado por Greg, o wstring é útil para a internacionalização, é quando você estará lançando seu produto em outros idiomas que não o inglês
4) Verifique isso em caracteres amplos http://en.wikipedia.org/wiki/Wide_character
fonte
Quando você NÃO deve usar caracteres largos?
Quando você está escrevendo um código antes do ano de 1990.
Obviamente, eu estou sendo louco, mas realmente, é o século 21 agora. 127 caracteres já deixaram de ser suficientes. Sim, você pode usar o UTF8, mas por que se preocupar com as dores de cabeça?
fonte
wchar_t
é que seu tamanho e significado são específicos do SO. Apenas troca os velhos problemas pelos novos. Enquanto achar
échar
independente do SO (em plataformas semelhantes, pelo menos). Assim, podemos usar o UTF-8, agrupar tudo em sequências dechar
s e lamentar como o C ++ nos deixa completamente por nossa conta, sem métodos padrão para medir, indexar, encontrar etc. nessas seqüências.wchar_t
é um tipo de dados de largura fixa; portanto, uma matriz de 10wchar_t
sempre ocuparásizeof(wchar_t) * 10
bytes da plataforma. E UTF-16 é uma codificação de largura variável na qual os caracteres podem ser compostos de 1 ou 2 pontos de código de 16 bits (e s / 16/8 / g para UTF-8).