Tenho visto muitas pessoas na comunidade C ++ (particularmente ## c ++ em freenode) se ressentirem do uso de wstrings
e wchar_t
, e seu uso na API do Windows. O que é exatamente "errado" com wchar_t
e wstring
, e se eu quero apoiar a internacionalização, quais são algumas alternativas para personagens amplos?
86
Respostas:
O que é wchar_t?
wchar_t é definido de forma que qualquer codificação char locale possa ser convertida em uma representação wchar_t em que cada wchar_t representa exatamente um ponto de código:
Isso não requer que wchar_t seja grande o suficiente para representar qualquer caractere de todas as localidades simultaneamente. Ou seja, a codificação usada para wchar_t pode ser diferente entre os locais. O que significa que você não pode necessariamente converter uma string em wchar_t usando uma localidade e depois converter de volta para char usando outra localidade. 1
Visto que usar wchar_t como uma representação comum entre todas as localidades parece ser o uso principal de wchar_t na prática, você pode se perguntar para que serve, senão isso.
A intenção e o propósito original de wchar_t era tornar o processamento de texto simples, definindo-o de forma que ele exigisse um mapeamento um-para-um das unidades de código de uma string para os caracteres do texto, permitindo assim o uso dos mesmos algoritmos simples que são usados com strings ascii para trabalhar com outros idiomas.
Infelizmente, o texto da especificação wchar_t pressupõe um mapeamento um a um entre caracteres e pontos de código para conseguir isso. O Unicode quebra essa suposição 2 , portanto, você também não pode usar wchar_t com segurança para algoritmos de texto simples.
Isso significa que o software portátil não pode usar wchar_t como uma representação comum para texto entre localidades ou para permitir o uso de algoritmos de texto simples.
Qual é a utilidade de wchar_t hoje?
Não muito, pelo menos para código portátil. Se
__STDC_ISO_10646__
for definido, os valores de wchar_t representam diretamente os pontos de código Unicode com os mesmos valores em todos os locais. Isso torna seguro fazer as conversões entre locais mencionadas anteriormente. No entanto, você não pode confiar apenas nele para decidir que pode usar wchar_t dessa forma porque, embora a maioria das plataformas unix o defina, o Windows não o faz, embora o Windows use o mesmo local wchar_t em todos os locais.O motivo pelo qual o Windows não define
__STDC_ISO_10646__
é porque o Windows usa UTF-16 como sua codificação wchar_t e porque UTF-16 usa pares substitutos para representar pontos de código maiores que U + FFFF, o que significa que UTF-16 não atende aos requisitos para__STDC_ISO_10646__
.Para o código específico da plataforma, wchar_t pode ser mais útil. É essencialmente necessário no Windows (por exemplo, alguns arquivos simplesmente não podem ser abertos sem o uso de nomes de arquivo wchar_t), embora o Windows seja a única plataforma onde isso é verdade até onde eu sei (então talvez possamos pensar em wchar_t como 'Windows_char_t').
Em retrospectiva, wchar_t claramente não é útil para simplificar o tratamento de texto ou como armazenamento para texto independente de localidade. O código portátil não deve tentar usá-lo para esses fins. O código não portátil pode ser útil simplesmente porque alguma API o exige.
Alternativas
A alternativa que eu gosto é usar strings C codificadas em UTF-8, mesmo em plataformas não particularmente amigáveis para UTF-8.
Desta forma, pode-se escrever código portátil usando uma representação de texto comum entre plataformas, usar tipos de dados padrão para os fins pretendidos, obter o suporte da linguagem para esses tipos (por exemplo, literais de string, embora alguns truques sejam necessários para fazê-lo funcionar para alguns compiladores), alguns suporte a biblioteca padrão, suporte a depurador (mais truques podem ser necessários), etc. Com caracteres largos é geralmente mais difícil ou impossível de obter tudo isso, e você pode obter peças diferentes em plataformas diferentes.
Uma coisa que o UTF-8 não oferece é a capacidade de usar algoritmos de texto simples, como os possíveis com ASCII. Neste UTF-8 não é pior do que qualquer outra codificação Unicode. Na verdade, pode ser considerado melhor porque as representações de unidades de vários códigos em UTF-8 são mais comuns e, portanto, os bugs no código que lida com essas representações de largura variável de caracteres são mais prováveis de serem notados e corrigidos do que se você tentar manter o UTF -32 com NFC ou NFKC.
Muitas plataformas usam UTF-8 como codificação nativa de caracteres e muitos programas não requerem nenhum processamento de texto significativo, portanto, escrever um programa internacionalizado nessas plataformas é um pouco diferente de escrever código sem considerar a internacionalização. Escrever um código mais amplamente portátil ou em outras plataformas requer a inserção de conversões nos limites de APIs que usam outras codificações.
Outra alternativa usada por alguns softwares é escolher uma representação de plataforma cruzada, como matrizes curtas não assinadas contendo dados UTF-16, e então fornecer todo o suporte da biblioteca e simplesmente viver com os custos de suporte de linguagem, etc.
C ++ 11 adiciona novos tipos de caracteres largos como alternativas para wchar_t, char16_t e char32_t com recursos de linguagem / biblioteca associados. Na verdade, não há garantia de que sejam UTF-16 e UTF-32, mas não imagino que nenhuma implementação principal use outra coisa. C ++ 11 também melhora o suporte UTF-8, por exemplo, com literais de string UTF-8, então não será necessário enganar o VC ++ para produzir strings codificadas em UTF-8 (embora eu possa continuar a fazer isso em vez de usar o
u8
prefixo) .Alternativas a evitar
TCHAR: TCHAR é para migrar programas antigos do Windows que assumem codificações legadas de char para wchar_t e é melhor esquecê-lo, a menos que seu programa tenha sido escrito em algum milênio anterior. Não é portátil e é inerentemente inespecífico sobre sua codificação e até mesmo seu tipo de dados, tornando-o inutilizável com qualquer API não baseada em TCHAR. Como seu propósito é a migração para wchar_t, o que vimos acima não é uma boa ideia, não há nenhum valor em usar o TCHAR.
1. Os caracteres que são representáveis em cadeias wchar_t, mas que não são suportados em qualquer local, não precisam ser representados com um único valor wchar_t. Isso significa que wchar_t poderia usar uma codificação de largura variável para certos caracteres, outra violação clara da intenção de wchar_t. Embora seja discutível que um caractere sendo representado por wchar_t é suficiente para dizer que a localidade 'suporta' esse caractere, caso em que codificações de largura variável não são legais e o uso de UTF-16 pelo Windows não está em conformidade.
2. O Unicode permite que muitos caracteres sejam representados com vários pontos de código, o que cria os mesmos problemas para algoritmos de texto simples como codificações de largura variável. Mesmo que se mantenha estritamente uma normalização composta, alguns caracteres ainda requerem vários pontos de código. Veja: http://www.unicode.org/standard/where/
fonte
fopen
um arquivo cujo nome contenha caracteres não ANSI.Não há nada "errado" com wchar_t. O problema é que, nos dias do NT 3.x, a Microsoft decidiu que o Unicode era bom (é) e implementou o Unicode como caracteres wchar_t de 16 bits. Portanto, a maior parte da literatura da Microsoft de meados dos anos 90 equivalia a Unicode == utf16 == wchar_t.
O que, infelizmente, não é o caso. "Caracteres largos" não são necessariamente 2 bytes, em todas as plataformas, em todas as circunstâncias.
Este é um dos melhores primers em "Unicode" (independente desta questão, independente de C ++) que eu já vi: Eu recomendo fortemente :
E eu honestamente acredito que a melhor maneira de lidar com "ASCII de 8 bits" vs "caracteres amplos do Win32" vs "wchar_t-in-general" é simplesmente aceitar que "Windows é diferente" ... e codificar de acordo.
NA MINHA HUMILDE OPINIÃO...
PS:
Eu concordo totalmente com jamesdlin acima:
fonte