std :: wstring VS std :: string

742

Não consigo entender as diferenças entre std::stringe std::wstring. Eu sei que wstringsuporta caracteres largos, como caracteres Unicode. Eu tenho as seguintes perguntas:

  1. Quando devo usar std::wstringmais std::string?
  2. Pode std::stringconter todo o conjunto de caracteres ASCII, incluindo os caracteres especiais?
  3. É std::wstringsuportado por todos os compiladores C ++ populares?
  4. O que é exatamente um " caractere amplo "?
Rapptz
fonte
10
O conjunto de caracteres ASCII não possui muitos caracteres "especiais", o mais exótico é provavelmente `(aspas). std :: string pode conter cerca de 0,025% de todos os caracteres Unicode (normalmente, 8 bit carvão animal)
MSalters
3
Boas informações sobre caracteres largos e qual o tipo a ser usado podem ser encontradas aqui: programmers.stackexchange.com/questions/102205/…
Yariv
14
Bem, e como estamos em 2012, o utf8everywhere.org foi escrito. Ele praticamente responde a todas as perguntas sobre direitos e erros com C ++ / Windows.
Pavel Radzivilovsky
42
@MSalters: std :: string pode conter 100% de todos os caracteres Unicode, mesmo que CHAR_BIT seja 8. Depende da codificação de std :: string, que pode ser UTF-8 no nível do sistema (como em quase todos os lugares, exceto no Windows ) ou no nível do seu aplicativo. A codificação nativa estreita não suporta Unicode? Não tem problema, apenas não use, use UTF-8.
Yakov Galka
8
Ótima leitura sobre este tópico: utf8everywhere.org
Timothy Shields

Respostas:

993

string? wstring?

std::stringé um basic_stringmodelo em um chare std::wstringem um wchar_t.

char vs. wchar_t

chardeve conter um caractere, geralmente um caractere de 8 bits.
wchar_tdeve 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 charnem wchar_testá 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:

#include <cstring>
#include <iostream>

int main(int argc, char* argv[])
{
   const char text[] = "olé" ;


   std::cout << "sizeof(char)    : " << sizeof(char) << std::endl ;
   std::cout << "text            : " << text << std::endl ;
   std::cout << "sizeof(text)    : " << sizeof(text) << std::endl ;
   std::cout << "strlen(text)    : " << strlen(text) << std::endl ;

   std::cout << "text(ordinals)  :" ;

   for(size_t i = 0, iMax = strlen(text); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned char>(text[i])
                          );
   }

   std::cout << std::endl << std::endl ;

   // - - - 

   const wchar_t wtext[] = L"olé" ;

   std::cout << "sizeof(wchar_t) : " << sizeof(wchar_t) << std::endl ;
   //std::cout << "wtext           : " << wtext << std::endl ; <- error
   std::cout << "wtext           : UNABLE TO CONVERT NATIVELY." << std::endl ;
   std::wcout << L"wtext           : " << wtext << std::endl;

   std::cout << "sizeof(wtext)   : " << sizeof(wtext) << std::endl ;
   std::cout << "wcslen(wtext)   : " << wcslen(wtext) << std::endl ;

   std::cout << "wtext(ordinals) :" ;

   for(size_t i = 0, iMax = wcslen(wtext); i < iMax; ++i)
   {
      std::cout << " " << static_cast<unsigned int>(
                              static_cast<unsigned short>(wtext[i])
                              );
   }

   std::cout << std::endl << std::endl ;

   return 0;
}

gera o seguinte texto:

sizeof(char)    : 1
text            : olé
sizeof(text)    : 5
strlen(text)    : 4
text(ordinals)  : 111 108 195 169

sizeof(wchar_t) : 4
wtext           : UNABLE TO CONVERT NATIVELY.
wtext           : ol�
sizeof(wtext)   : 16
wcslen(wtext)   : 3
wtext(ordinals) : 111 108 233

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 charno Linux, você geralmente deve usar o Unicode sem nem mesmo saber. E, como std::stringfunciona char, std::stringjá 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 chare 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_t2 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 charsão denominados "multibyte" (porque cada glifo é composto de um ou mais chars), enquanto os aplicativos que usam wchar_tsã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 com wchar_tcadeias de caracteres, portanto, mesmo aplicativos históricos terão suas charcadeias de caracteres convertidas wchar_tao usar API como SetWindowText()(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

  1. 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

  2. Pode std::stringconter 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::stringserá suficiente para manipular charcadeias baseadas em todas (cada charuma sendo um número de 0 a 255). Mas:

    1. O ASCII deve ir de 0 a 127. Os maiores charNÃO são ASCII.
    2. um charde 0 a 127 será mantido corretamente
    3. um charde 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.
  3. É std::wstringsuportado 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.

  4. O que é exatamente um personagem amplo?

    No C / C ++, é um tipo de caractere escrito wchar_tque é maior que o chartipo de caractere simples . Ele deve ser usado para inserir caracteres cujos índices (como glifos Unicode) são maiores que 255 (ou 127, dependendo ...).

paercebal
fonte
4
@gnud: Talvez o wchar_t fosse o suficiente para lidar com todos os caracteres UCS-2 (a maioria dos caracteres UTF-16) antes do advento do UTF-16 ... Ou talvez a Microsoft tivesse outras prioridades além do POSIX, como facilitar o acesso ao Unicode sem modificar o uso de char paginado por código no Win32.
paercebal
4
@Sorin Sbarnea: UTF-8 pode levar de 1 a 6 bytes, mas aparentemente o padrão a limita a 1 a 4. Consulte en.wikipedia.org/wiki/UTF8#Description para obter mais informações.
paercebal
8
Embora esses exemplos produzam resultados diferentes no Linux e no Windows, o programa C ++ contém um comportamento definido pela implementação sobre se olèé codificado como UTF-8 ou não. Ainda mais, a razão que você não pode nativamente transmitir wchar_t *aos std::coutocorre 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ê usa std::stringou std::wstringdepende da sua preferência de codificação e não da plataforma, especialmente se deseja que seu código seja portátil.
John Leidegren
14
O Windows realmente usa UTF-16 e já existe há algum tempo; versões mais antigas do Windows usavam o UCS-2, mas esse não é mais o caso. Meu único problema aqui é a conclusão que std::wstringdeve 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.
John Leidegren
15
@ John Leidegren:: If your only concern was calling into the Unicode Windows API and not marshalling strings then sureEntã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 WinAPI std::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.
paercebal
71

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:

  1. Algumas razões fracas. Existe por razões históricas, onde se acreditava que os widechars eram a maneira correta de oferecer suporte ao Unicode. Agora é usado para fazer interface com APIs que preferem cadeias UTF-16. Eu os uso apenas nas proximidades diretas dessas chamadas de API.
  2. Isso não tem nada a ver com std :: string. Pode conter qualquer codificação que você colocar nela. A única questão é como você trata seu conteúdo. Minha recomendação é UTF-8, portanto, será capaz de armazenar todos os caracteres Unicode corretamente. É uma prática comum no Linux, mas acho que os programas do Windows também devem fazê-lo.
  3. Não.
  4. Caractere amplo é um nome confuso. Nos primeiros dias do Unicode, havia a crença de que um caractere pode ser codificado em dois bytes, daí o nome. Hoje, significa "qualquer parte do caractere com dois bytes de comprimento". UTF-16 é visto como uma sequência desses pares de bytes (também conhecidos como caracteres largos). Um caractere em UTF-16 leva um ou dois pares.
Pavel Radzivilovsky
fonte
37

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:

  1. 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)

  2. use std :: string para qualquer string UTF-8 (apenas a typedef std::string UTF8String)

  3. 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).

  4. 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 ...).

  5. 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.

  6. adicione duas funções utilitárias para converter entre UTF-8 e UCS-2:

    UCS2String ConvertToUCS2( const UTF8String &str );
    UTF8String ConvertToUTF8( const UCS2String &str );

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.

Frunsi
fonte
Caramba, não é bom saber que o suporte nativo a Unicode não existe.
Mihai Danila
@ Frunsi, estou curioso para saber se você já tentou o Glib :: ustring e, em caso afirmativo, quais são seus pensamentos?
Caroline Beltran
@CarolineBeltran: Conheço o Glib, mas nunca o usei, e provavelmente nunca o usarei, porque é bastante limitado a uma plataforma-alvo inespecífica (sistemas unixóides ...). Sua porta do Windows é baseada na camada win2unix externa, e o IMHO não possui nenhuma camada de compatibilidade com o OSX. Todo este material está dirigindo claramente em uma direção errada, pelo menos para o meu código (neste nível arco ...) ;-) Então, Glib não é uma opção
Frunsi
9
Pesquisar, substituir etc. funciona bem em cadeias UTF-8 (uma parte da sequência de bytes que representa um caractere nunca pode ser mal interpretada como outro caractere). De fato, UTF-16 e UTF-32 não facilitam nada: as três codificações são codificações multibyte na prática, porque um caractere percebido pelo usuário (cluster grafema) pode ter qualquer número de pontos de código unicode! A solução pragmática é usar o UTF-8 para tudo e converter em UTF-16 somente ao lidar com a API do Windows.
Daniel
5
@Frunsi: pesquisar e substituir funciona tão bem com UTF-8 quanto com UTF-32. É precisamente porque o processamento de texto adequado para Unicode precisa lidar com 'caracteres' com vários códigos, que o uso de uma codificação de comprimento variável como UTF-8 não torna o processamento de strings mais complicado. Então, basta usar UTF-8 em qualquer lugar. As funções normais da string C funcionarão bem no UTF-8 (e corresponderão às comparações ordinais na string Unicode) e, se você precisar de algo mais sensível ao idioma, precisará chamar a biblioteca Unicode de qualquer maneira, UTF-16/32 não posso te salvar disso.
Daniel
25
  1. Quando você deseja ter caracteres largos armazenados em sua string. widedepende 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 bem std::stringusando 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-32como uma codificação unicode e poderá armazenar e manipular cadeias unicode usando uma codificação fixa (utf-32 é comprimento fixo). Isto significa do seu wstring s.size()função irá , em seguida, retornar a quantidade certa de elementos wchar_t e caracteres lógicos.

  2. Sim, o caractere sempre tem pelo menos 8 bits, o que significa que ele pode armazenar todos os valores ASCII.
  3. Sim, todos os principais compiladores são compatíveis.
Johannes Schaub - litb
fonte
Estou curioso sobre o # 2. Eu pensei que 7 bits seria tecnicamente válido também? Ou é necessário poder armazenar qualquer coisa além de caracteres ASCII de 7 bits?
31868 jalf
1
sim, jalf. c89 especifica intervalos mínimos para tipos básicos em sua documentação de limits.h (para caracteres não assinados, são 0..255 min) e um sistema binário puro para tipos inteiros. segue char, char não assinado e char assinado têm comprimentos de bit mínimos de 8. c ++ herda essas regras.
Johannes Schaub - litb 31/12/08
15
"Isso significa que a função s.size () do seu wstring retornará a quantidade certa de elementos wchar_t e caracteres lógicos." Isso não é totalmente preciso, mesmo para Unicode. Seria mais preciso dizer o ponto de código do que "caractere lógico", mesmo no UTF-32, um determinado caractere pode ser composto de vários pontos de código.
Logan Capaldo
Vocês estão essencialmente dizendo que o C ++ não tem suporte nativo para o conjunto de caracteres Unicode?
Mihai Danila
1
"Mas não entenderá o significado dos pontos de código unicode". Nas janelas, também não std::wstring.
Deduplicator
5

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
1
Juan: Você quer dizer que std :: string pode conter todos os caracteres unicode, mas o comprimento será reportado incorretamente? Existe uma razão para que esteja relatando tamanho incorreto?
3
Ao usar a codificação utf-8, um único caractere unicode pode ser composto de vários bytes. É por isso que a codificação utf-8 é menor quando se usa principalmente caracteres do conjunto ascii padrão. Você precisa usar funções especiais (ou criar suas próprias) para medir o número de caracteres unicode.
2
(Específico do Windows) A maioria das funções espera que uma string usando bytes seja ASCII e 2 bytes seja Unicode, versões mais antigas MBCS. O que significa que, se você estiver armazenando unicode de 8 bits, precisará convertê-lo em unicode de 16 bits para chamar uma função padrão do Windows (a menos que esteja usando apenas a parte ASCII).
Greg Domjan 31/12/08
2
Uma std :: string não apenas reportará o comprimento incorretamente, mas também produzirá a string incorreta. Se algum caractere Unicode é representado em UTF-8 como vários bytes, que std :: string considera seus próprios caracteres, suas rotinas de manipulação normalmente std :: string provavelmente produzirão vários caracteres estranhos que resultam da má interpretação daquele. caráter correto.
Mihai Danila
2
Sugiro alterar a resposta para indicar que as strings devem ser consideradas apenas contêineres de bytes e, se os bytes forem de alguma codificação Unicode (UTF-8, UTF-16, ...), use bibliotecas específicas que entendam aquele. As APIs padrão baseadas em string (comprimento, substr etc.) falharão miseravelmente com caracteres multibyte. Se essa atualização for feita, removerei meu voto negativo.
Mihai Danila
4
  1. Quando você deseja armazenar caracteres 'largos' (Unicode).
  2. Sim: 255 deles (excluindo 0).
  3. Sim.
  4. Aqui está um artigo introdutório: http://www.joelonsoftware.com/articles/Unicode.html
ChrisW
fonte
11
std :: string pode conter 0 muito bem (apenas tome cuidado se você chamar o método c_str ()) #
1155 Mr Fooz
3
E, estritamente falando, não é garantido que um char tenha 8 bits. :) Seu link no número 4 é uma leitura obrigatória, mas acho que não responde à pergunta. Um caractere amplo não tem nada a ver com unicode. É simplesmente um personagem mais amplo. (Quanto mais amplo depende OS, mas normalmente 16 ou 32 bits)
jalf
2
  1. quando você deseja usar strings Unicode e não apenas ascii, útil para internacionalização
  2. sim, mas não joga bem com 0
  3. não tem conhecimento de nenhum que não
  4. caractere amplo é a maneira específica do compilador de lidar com a representação de comprimento fixo de um caractere unicode, para MSVC é um caractere de 2 bytes, para o gcc eu entendo que é de 4 bytes. e um +1 para http://www.joelonsoftware.com/articles/Unicode.html
Greg Domjan
fonte
1
2. Um std :: string pode conter um caractere NULL muito bem. Ele também pode conter utf-8 e caracteres largos também.
@ Juan: Isso me colocou em confusão novamente. Se std :: string pode manter caracteres unicode, o que é especial com std :: wstring?
1
@Appu: std :: string pode conter caracteres unicode UTF-8. Existem vários padrões unicode direcionados para diferentes larguras de caracteres. UTf8 tem 8 bits de largura. Há também UTF-16 e UTF-32 com 16 e 32 bits de largura, respectivamente
Greg D
Com um std :: wstring. Cada caractere unicode pode ser um wchar_t ao usar as codificações de comprimento fixo. Por exemplo, se você optar por usar a abordagem joel on software como Greg vincula. Em seguida, o comprimento da string é exatamente o número de caracteres unicode na string. Mas ocupa mais espaço
Eu não disse que não poderia conter um 0 '\ 0', e o que eu quis dizer com não funciona bem é que alguns métodos podem não fornecer o resultado esperado que contém todos os dados do wstring. Tão severo nos votos negativos.
Greg Domjan 31/12/08
2

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 stringe a wstringé o tipo de dados dos caracteres que eles armazenam. Uma string armazena chars 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.

Seppo Enarvi
fonte
Portanto, parafraseando o primeiro parágrafo: o aplicativo que precisa de mais de 256 caracteres precisa usar uma codificação multibyte ou codificação maybe_multibyte.
Deduplicator
Geralmente, codificações de 16 e 32 bits, como UCS-2 e UCS-4, não são chamadas de codificações multibyte. O padrão C ++ distingue entre codificações multibyte e caracteres largos. Uma representação ampla de caracteres usa um número fixo (geralmente mais de 8) bits por caractere. Codificações que usam um único byte para codificar os caracteres mais comuns e vários bytes para codificar o restante do conjunto de caracteres são chamados de codificações multibyte.
Seppo Enarvi
Desculpe, comentário desleixado. Deveria ter dito a codificação de comprimento variável. UTF-16 é uma codificação de comprimento variável, assim como UTF-8. Fingir que não é uma ideia.
Deduplicator
Este é um bom ponto. Não há motivo para o wstrings não poder ser usado para armazenar UTF-16 (em vez do UCS-2), mas a conveniência de uma codificação de comprimento fixo é perdida.
Seppo Enarvi
2

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)

Leiyi.China
fonte
0

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::stringqualquer lugar. No Windows / VS, fica mais complexo. Aqui está o porquê. O Windows espera que as seqüências armazenadas usando chars 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 e std::coutuso de <<um nome de arquivo para std::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, Ltornando-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 um std::wstringconstrutor ou convertê-la em utf-8 e colocá-la em a std::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 usar std::string, não, std::wstringe o texto deve ser codificado usando sua página de código do código de idioma. Se você tiver um std::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 Microsoft std::fstreamque 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::stringqualquer lugar.

Se você está apenas trabalhando no Windows, use o UCS2 em std::wstringqualquer 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::wstringqualquer lugar do Linux, pode não ter acesso à versão ampla std::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 unicodestringcomo std::stringno Linux e std::wstringno Windows, e ter uma macro chamada UNI () que prefixa L no Windows e nada no Linux, depois o código

#include <fstream>
#include <string>
#include <iostream>
#include <Windows.h>

#ifdef _WIN32
typedef std::wstring unicodestring;
#define UNI(text) L ## text
std::string formatForConsole(const unicodestring &str)
{
    std::string result;
    //Call WideCharToMultiByte to do the conversion
    return result;
}
#else
typedef std::string unicodestring;
#define UNI(text) text
std::string formatForConsole(const unicodestring &str)
{
    return str;
}
#endif

int main()
{

    unicodestring fileName(UNI("fileName"));
    std::ofstream fout;
    fout.open(fileName);
    std::cout << formatForConsole(fileName) << std::endl;
    return 0;
}

ficaria 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 #ifdefspara 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::stringsejam 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_tvez dechar

4) Um caractere amplo é um tipo de caractere maior que o chartipo padrão de 1 byte . No Windows, são 2 bytes, no Linux, são 4 bytes.

Phil Rosenberg
fonte
1
Em relação a "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 por?." -> Não acho que isso seja verdade quando o compilador usa a codificação UTF-8 (use /utf-8).
Roi Danton
Eu não estava ciente disso como uma opção. Nesse link docs.microsoft.com/en-us/cpp/build/reference/… parece que não há uma caixa de seleção para selecionar nas propriedades do projeto, você deve adicioná-lo como uma opção de linha de comando adicional. Bom lugar!
Phil Rosenberg
-2

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

Raghu
fonte
-6

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
16
@dave: Não sei que dor de cabeça o UTF-8 cria, que é maior que a do Widechars (UTF-16). no UTF-16, você também possui caracteres com vários caracteres.
Pavel Radzivilovsky
O problema é que, se você estiver em qualquer lugar que não seja o país de língua inglesa, você PRECISA usar o wchar_t. Sem mencionar que alguns alfabetos têm muito mais caracteres do que você pode caber em um byte. Nós estávamos lá, no DOS. Codepage esquizofrenia, não, obrigado, não mais ..
Swift - sexta-feira
1
@ Swift O problema wchar_té que seu tamanho e significado são específicos do SO. Apenas troca os velhos problemas pelos novos. Enquanto a charé charindependente do SO (em plataformas semelhantes, pelo menos). Assim, podemos usar o UTF-8, agrupar tudo em sequências de chars e lamentar como o C ++ nos deixa completamente por nossa conta, sem métodos padrão para medir, indexar, encontrar etc. nessas seqüências.
Underscore_d
1
@ Swift Você parece tê-lo completamente ao contrário. wchar_té um tipo de dados de largura fixa; portanto, uma matriz de 10 wchar_tsempre ocupará sizeof(wchar_t) * 10bytes 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).
Underscore_d
1
A representação @SteveHollasch wchar_t da cadeia de caracteres no Windows codificaria caracteres maiores que FFFF como um par substituto especial; outros precisariam apenas de um elemento wchar_t. Portanto, essa representação não será compatível com a representação criada pelo compilador gnu (onde todos os caracteres menores que FFFF terão zero palavra na frente deles). O que é armazenado em wchar_t é determinada pelo programador e compilador, não por algum acordo
Swift - Sexta-feira Pie