"Std :: string faz o que deveria?" O que você acha que deveria fazer?
R. Martinho Fernandes
2
Eu uso utfcpp.sourceforge.net para minhas necessidades de utf8. É um arquivo de cabeçalho simples que fornece iteradores para strings unicode.
Fscan #
2
std :: string deve armazenar bytes, isto é, a sequência da unidade de código da codificação UTF-8. Sim, faz exatamente isso, desde o começo. utf8everywhere.org
Pavel Radzivilovsky
3
Os maiores problemas em potencial com o suporte a Unicode estão no Unicode e seu uso na própria tecnologia da informação. O Unicode não é adequado (e não foi projetado) para o que é usado. O Unicode foi projetado para reproduzir todos os glifos possíveis que foram escritos em algum lugar por alguém, em algum momento com todas as nuances improváveis e pedantes possíveis, incluindo 3 ou 4 significados diferentes e 3 ou 4 maneiras diferentes de compor o mesmo glifo. Não é para ser útil para ser usado no idioma do dia a dia, nem para ser aplicável ou para ser processado de maneira fácil ou sem ambiguidade.
Damon
11
Sim, ele foi projetado para ser usado na linguagem cotidiana. Mine pelo menos. E o seu provavelmente também. Acontece que processar texto humano de uma maneira geral é uma tarefa muito difícil. Nem sequer é possível definir sem ambiguidade o que é um personagem. A reprodução geral de glifos nem sequer faz parte do estatuto Unicode.
precisa saber é o seguinte
Respostas:
267
Quão bem a biblioteca padrão C ++ suporta unicode?
Terrivelmente.
Uma rápida varredura nos recursos da biblioteca que podem fornecer suporte a Unicode me fornece esta lista:
Biblioteca de strings
Biblioteca de localização
Biblioteca de entrada / saída
Biblioteca de expressões regulares
Acho que todos, exceto o primeiro, fornecem um apoio terrível. Voltarei a ele com mais detalhes após um rápido desvio de suas outras perguntas.
Faz std::stringo que deveria?
Sim. De acordo com o padrão C ++, é isso que std::stringseus irmãos devem fazer:
O modelo de classe basic_stringdescreve objetos que podem armazenar uma sequência que consiste em um número variável de objetos arbitrários do tipo char com o primeiro elemento da sequência na posição zero.
Bem, std::stringisso está bem? Isso fornece alguma funcionalidade específica para Unicode? Não.
Deveria? Provavelmente não. std::stringé bom como uma sequência de charobjetos. Isso é útil; o único incômodo é que é uma visão de texto de nível muito baixo e o C ++ padrão não fornece uma de nível superior.
Como eu uso isso?
Use-o como uma sequência de charobjetos; fingir que é outra coisa está fadado ao fim.
Onde estão os problemas em potencial?
Por todo o lugar? Vamos ver...
Biblioteca de strings
A biblioteca de strings nos fornece basic_string, que é apenas uma sequência do que o padrão chama de "objetos do tipo char". Eu os chamo de unidades de código. Se você deseja uma visualização de texto de alto nível, não é isso que você está procurando. Esta é uma exibição de texto adequada para serialização / desserialização / armazenamento.
Ele também fornece algumas ferramentas da biblioteca C que podem ser usadas para preencher a lacuna entre o mundo restrito e o mundo Unicode: c16rtomb/ mbrtoc16e c32rtomb/ mbrtoc32.
Biblioteca de localização
A biblioteca de localização ainda acredita que um desses "objetos semelhantes a caracteres" é igual a um "caractere". É claro que isso é bobagem e torna impossível que muitas coisas funcionem corretamente além de um pequeno subconjunto de Unicode, como o ASCII.
Considere, por exemplo, o que o padrão chama de "interfaces de conveniência" no <locale>cabeçalho:
Como você espera que qualquer uma dessas funções categorize adequadamente, digamos, U + 1F34C ʙᴀɴᴀɴᴀ, como em u8"🍌"ou u8"\U0001F34C"? Não há como isso funcione, porque essas funções recebem apenas uma unidade de código como entrada.
Isso poderia funcionar com um código de idioma apropriado se você usasse char32_tapenas: U'\U0001F34C'é uma única unidade de código no UTF-32.
No entanto, isso ainda significa que você só obtém as transformações simples de maiúsculas touppere minúsculas e tolowerque, por exemplo, não são boas o suficiente para algumas localidades alemãs: "ß" maiúsculas para "SS" ☦, mas toupperpode retornar apenas uma unidade de código de caracteres .
Em seguida, wstring_convert/ wbuffer_converte as facetas de conversão de código padrão.
wstring_converté usado para converter entre strings em uma determinada codificação em strings em outra determinada codificação. Existem dois tipos de sequência envolvidos nessa transformação, que o padrão chama de sequência de bytes e uma sequência ampla. Como esses termos são realmente enganosos, prefiro usar "serializado" e "desserializado", respectivamente, em vez disso †.
As codificações a serem convertidas são decididas por um codecvt (uma faceta de conversão de código) passado como um argumento de tipo de modelo para wstring_convert .
wbuffer_convertexecuta uma função semelhante, mas como um amplo buffer de fluxo desserializado que envolve um buffer de fluxo serializado de bytes . Qualquer E / S é executada através do buffer de fluxo serializado de bytes subjacente com conversões de e para as codificações fornecidas pelo argumento codecvt. Escrever serializa nesse buffer e depois grava a partir dele, e a leitura lê no buffer e desserializa a partir dele.
A norma fornece alguns modelos de classe codecvt para uso com essas facilidades: codecvt_utf8, codecvt_utf16, codecvt_utf8_utf16, e algumas codecvtespecializações. Juntas, essas facetas padrão fornecem todas as seguintes conversões. (Nota: na lista a seguir, a codificação à esquerda é sempre a sequência serializada / streambuf e a codificação à direita é sempre a sequência desserializada / streambuf; o padrão permite conversões em ambas as direções).
UTF-8 ↔ UCS-2 com codecvt_utf8<char16_t>e codecvt_utf8<wchar_t>onde sizeof(wchar_t) == 2;
UTF-8 ↔ UTF-32 com codecvt_utf8<char32_t>, codecvt<char32_t, char, mbstate_t>e codecvt_utf8<wchar_t>em que sizeof(wchar_t) == 4;
UTF-16 ↔ UCS-2 com codecvt_utf16<char16_t>e codecvt_utf16<wchar_t>onde sizeof(wchar_t) == 2;
UTF-16 ↔ UTF-32 com codecvt_utf16<char32_t>e codecvt_utf16<wchar_t>onde sizeof(wchar_t) == 4;
estreito ↔ largo com codecvt<wchar_t, char_t, mbstate_t>
no-op com codecvt<char, char, mbstate_t>.
Vários deles são úteis, mas há muitas coisas estranhas aqui.
Primeiro: santo alto substituto! esse esquema de nomeação é confuso.
Então, há muito suporte ao UCS-2. O UCS-2 é uma codificação do Unicode 1.0 que foi substituída em 1996 porque suporta apenas o plano multilíngue básico. Por que o comitê achou desejável se concentrar em uma codificação que foi substituída há mais de 20 anos, eu não sei ‡. Não é como se o suporte para mais codificações fosse ruim ou algo assim, mas o UCS-2 aparece com muita frequência aqui.
Eu diria que isso char16_té obviamente destinado ao armazenamento de unidades de código UTF-16. No entanto, essa é uma parte do padrão que pensa o contrário. codecvt_utf8<char16_t>não tem nada a ver com UTF-16. Por exemplo, wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")compilará bem, mas falhará incondicionalmente: a entrada será tratada como a sequência UCS-2u"\xD83C\xDF4C" , que não pode ser convertida em UTF-8 porque o UTF-8 não pode codificar nenhum valor no intervalo 0xD800-0xDFFF.
Ainda na frente do UCS-2, não há como ler um fluxo de bytes UTF-16 em uma string UTF-16 com essas facetas. Se você tiver uma sequência de bytes UTF-16, não poderá desserializá-la em uma sequência de char16_t. Isso é surpreendente, porque é mais ou menos uma conversão de identidade. Ainda mais surpreendente, porém, é o fato de que há suporte para desserialização de um fluxo UTF-16 para uma cadeia UCS-2 comcodecvt_utf16<char16_t> , o que na verdade é uma conversão com perdas.
Porém, o suporte a UTF-16-as-bytes é bastante bom: suporta a detecção de endianess de uma BOM ou a seleção explícita no código. Ele também suporta a produção de saída com e sem uma lista técnica.
Existem algumas possibilidades de conversão mais interessantes ausentes. Não há como desserializar de um fluxo ou sequência de bytes UTF-16 para uma sequência UTF-8, pois o UTF-8 nunca é suportado como a forma desserializada.
E aqui o mundo estreito / amplo é completamente separado do mundo UTF / UCS. Não há conversões entre as codificações estreitas / largas no estilo antigo e as codificações Unicode.
Biblioteca de entrada / saída
A biblioteca de E / S pode ser usada para ler e escrever texto em codificações Unicode usando os recursos wstring_converte wbuffer_convertdescritos acima. Eu não acho que há muito mais que precisaria ser suportado por essa parte da biblioteca padrão.
Biblioteca de expressões regulares
Eu expus problemas com regexes C ++ e Unicode no estouro de pilha antes. Não repetirei todos esses pontos aqui, mas apenas declaro que os regexes C ++ não têm suporte Unicode de nível 1, que é o mínimo necessário para torná-los utilizáveis sem recorrer ao uso de UTF-32 em todos os lugares.
É isso aí?
Sim é isso. Essa é a funcionalidade existente. Há muitas funcionalidades Unicode que não são vistas em nenhum lugar como algoritmos de normalização ou segmentação de texto.
U + 1F4A9 . Existe alguma maneira de obter um melhor suporte Unicode em C ++?
† Uma sequência de bytes é, sem surpresa, uma sequência de bytes, ou seja, charobjetos. No entanto, diferentemente de uma literal de cadeia ampla , que é sempre uma matriz de wchar_tobjetos, uma "cadeia ampla" nesse contexto não é necessariamente uma cadeia de wchar_tobjetos. De fato, o padrão nunca define explicitamente o que significa uma "cadeia ampla"; portanto, devemos adivinhar o significado do uso. Como a terminologia padrão é desleixada e confusa, eu uso a minha em nome da clareza.
Codificações como UTF-16 podem ser armazenadas como sequências de char16_t, as quais não possuem endianness; ou eles podem ser armazenados como sequências de bytes, que possuem endianness (cada par consecutivo de bytes pode representar um char16_tvalor diferente, dependendo da endianness). O padrão suporta esses dois formulários. Uma sequência de char16_té mais útil para manipulação interna no programa. Uma sequência de bytes é a maneira de trocar essas strings com o mundo externo. Os termos que vou usar em vez de "byte" e "wide" são, portanto, "serializados" e "desserializados".
‡ Se você está prestes a dizer "mas Windows!" segure seu 🐎🐎 . Todas as versões do Windows desde o Windows 2000 usam UTF-16.
☦ Sim, eu conheço as gromas Eszett (ẞ), mas mesmo que você altere todos os locais alemães da noite para o dia para ter maiúsculas em ẞ, ainda existem muitos outros casos em que isso falharia. Tente digitar em maiúsculas U + FB00 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟɪɢᴀᴛᴜʀᴇ ғғ. Não há ғғ ᴄᴀᴘɪᴛᴀʟ ʟɪɢᴀᴛᴜʀᴇ ғғ; apenas maiúsculas para dois Fs. Ou U + 01F0 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟᴇᴛᴛᴇʀ ᴊ ᴡɪᴛʜ ᴄᴀʀᴏɴ; não há capital pré-composto; apenas em maiúsculas para uma maiúscula J e uma combinação de caracteres.
Quanto mais eu leio sobre isso, mais tenho a sensação de não entender nada sobre tudo isso. Li a maioria dessas coisas há alguns meses e ainda sinto que estou descobrindo tudo de novo ... Para simplificar para o meu pobre cérebro que agora dói um pouco, todos esses conselhos sobre o utf8 em todos os lugares ainda são válidos, certo? Se eu "apenas" quiser que meus usuários possam abrir e gravar arquivos, independentemente das configurações do sistema, posso pedir o nome do arquivo, armazená-lo em uma string std :: e tudo deve funcionar corretamente, mesmo no Windows? Desculpe para pedir que (novamente) ...
Uflex
5
@Uflex Tudo o que você realmente pode fazer com std :: string é tratá-lo como um blob binário. Em uma implementação Unicode adequada, nem a codificação interna (porque está oculta nos detalhes da implementação) nem a externa (são importantes), você ainda precisa ter o codificador / decodificador disponível).
Cat Plus Plus
3
@Uflex talvez. Não sei se seguir um conselho que você não entende é uma boa ideia.
@ graham.reeds haha, obrigado, mas eu sabia disso. Verifique a seção "Agradecimentos";)
R. Martinho Fernandes
40
O Unicode não é suportado pela Biblioteca Padrão (para qualquer significado razoável de suportado).
std::stringnão é melhor do que std::vector<char>: é completamente alheio ao Unicode (ou qualquer outra representação / codificação) e simplesmente trata seu conteúdo como uma bolha de bytes.
Se você só precisa armazenar e gerar blobs , funciona muito bem; mas assim que desejar a funcionalidade Unicode (número de pontos de código , número de grafemas etc.), você estará sem sorte.
A única biblioteca abrangente que conheço é a UTI . A interface C ++ foi derivada da Java, portanto, está longe de ser idiomática.
@Uflex: na página que você vinculou Para atingir esse objetivo, o Boost.Locale usa a moderna biblioteca Unicode e Localização: ICU - International Components for Unicode.
@SuperflyJon: É verdade, mas de acordo com a mesma página, o suporte ao Unicode para os back-ends que não são da UTI é "severamente limitado".
Matthieu M.
24
Você pode armazenar UTF-8 com segurança em um std::string(ou em um char[]ou char*, nesse caso), devido ao fato de que um NUL Unicode (U + 0000) é um byte nulo no UTF-8 e que essa é a única maneira de um nulo. byte pode ocorrer em UTF-8. Portanto, suas cadeias UTF-8 serão finalizadas corretamente de acordo com todas as funções de cadeia C e C ++, e você poderá usá-las com iostreams C ++ (incluindo std::coute std::cerr, desde que seu código de idioma seja UTF-8).
O que você não pode fazer com o std::stringUTF-8 é obter comprimento em pontos de código. std::string::size()informará o comprimento da string em bytes , que é apenas igual ao número de pontos de código quando você estiver no subconjunto ASCII do UTF-8.
Se você precisar operar em cadeias UTF-8 no nível do ponto de código (ou seja, não apenas armazená-las e imprimi-las) ou se estiver lidando com a UTF-16, que provavelmente possui muitos bytes nulos internos, é necessário examinar os tipos de cadeia de caracteres largos.
std::stringpode ser lançado em iostreams com nulos incorporados.
R. Martinho Fernandes
3
É totalmente planejado. Não quebra c_str(), porque size()ainda funciona. Apenas APIs quebradas (ou seja, aquelas que não conseguem lidar com nulos incorporados, como a maioria do mundo C) são interrompidas.
R. Martinho Fernandes
1
Nulos incorporados quebram c_str()porque c_str()é suposto retornar os dados como uma cadeia C terminada em nulo - o que é impossível, devido ao fato de que as cadeias C não podem ter nulos incorporados.
uckelman
4
Não mais. c_str()agora simplesmente retorna o mesmo que data(), ou seja, tudo isso. APIs que assumem um tamanho podem consumi-lo. APIs que não, não podem.
R. Martinho Fernandes
6
Com a pequena diferença que c_str()garante que o resultado seja seguido por um objeto NUL do tipo char, e acho que data()não. Não, parece que data()agora faz isso também. (Claro, isso não é necessário para a APIs que consomem o tamanho, em vez de inferir que a partir de uma pesquisa terminator)
Infelizmente, o suporte na biblioteca padrão para codificações não uniformes (como UTF-8) ainda é ruim. Por exemplo, não há uma maneira agradável de obter o comprimento (em pontos de código) de uma string UTF-8.
Então, ainda precisamos usar std :: wstring para nomes de arquivos, se quisermos suportar idiomas não latinos? Porque as novas strings literais realmente não ajudam aqui como a corda geralmente vêm do usuário ...
Uflex
7
@Uflex std::stringpode conter uma string UTF-8 sem problemas, mas, por exemplo, o lengthmétodo retorna o número de bytes na string e não o número de pontos de código.
Algum programador,
8
Para ser honesto, obter o comprimento em pontos de código de uma string não tem muitos usos. O comprimento em bytes pode ser usado para pré-alocar corretamente os buffers, por exemplo.
R. Martinho Fernandes
2
O número de pontos de código em uma string UTF-8 não é um número muito interessante: pode-se escrever ñcomo 'LETRA PEQUENA LATINA N COM TILDE' (U + 00F1) (que é um ponto de código) ou 'LETRA PEQUENA LATINA N' ( U + 006E) seguido de 'COMBINING TILDE' (U + 0303), que é dois pontos de código.
Martin Bonner apoia Monica em
Todos esses comentários sobre "você não precisa disso e não precisa disso", como "número de pontos de código sem importância" etc., me parecem um tanto desconfortáveis. Depois de escrever um analisador que supostamente analisa o código-fonte utf8, depende da especificação do analisador se considera ou não LATIN SMALL LETTER N' == (U+006E) followed by 'COMBINING TILDE' (U+0303).
BitTickler 13/08/19
4
No entanto, existe uma biblioteca bastante útil chamada tiny-utf8 , que é basicamente um substituto para std::string/ std::wstring. Ele visa preencher a lacuna da classe de contêiner utf8-string ainda ausente.
Essa pode ser a maneira mais confortável de 'lidar' com strings utf8 (ou seja, sem normalização unicode e coisas semelhantes). Você opera confortavelmente em pontos de código , enquanto sua string permanece codificada em s codificados em comprimento de execução char.
Respostas:
Terrivelmente.
Uma rápida varredura nos recursos da biblioteca que podem fornecer suporte a Unicode me fornece esta lista:
Acho que todos, exceto o primeiro, fornecem um apoio terrível. Voltarei a ele com mais detalhes após um rápido desvio de suas outras perguntas.
Sim. De acordo com o padrão C ++, é isso que
std::string
seus irmãos devem fazer:Bem,
std::string
isso está bem? Isso fornece alguma funcionalidade específica para Unicode? Não.Deveria? Provavelmente não.
std::string
é bom como uma sequência dechar
objetos. Isso é útil; o único incômodo é que é uma visão de texto de nível muito baixo e o C ++ padrão não fornece uma de nível superior.Use-o como uma sequência de
char
objetos; fingir que é outra coisa está fadado ao fim.Por todo o lugar? Vamos ver...
Biblioteca de strings
A biblioteca de strings nos fornece
basic_string
, que é apenas uma sequência do que o padrão chama de "objetos do tipo char". Eu os chamo de unidades de código. Se você deseja uma visualização de texto de alto nível, não é isso que você está procurando. Esta é uma exibição de texto adequada para serialização / desserialização / armazenamento.Ele também fornece algumas ferramentas da biblioteca C que podem ser usadas para preencher a lacuna entre o mundo restrito e o mundo Unicode:
c16rtomb
/mbrtoc16
ec32rtomb
/mbrtoc32
.Biblioteca de localização
A biblioteca de localização ainda acredita que um desses "objetos semelhantes a caracteres" é igual a um "caractere". É claro que isso é bobagem e torna impossível que muitas coisas funcionem corretamente além de um pequeno subconjunto de Unicode, como o ASCII.
Considere, por exemplo, o que o padrão chama de "interfaces de conveniência" no
<locale>
cabeçalho:Como você espera que qualquer uma dessas funções categorize adequadamente, digamos, U + 1F34C ʙᴀɴᴀɴᴀ, como em
u8"🍌"
ouu8"\U0001F34C"
? Não há como isso funcione, porque essas funções recebem apenas uma unidade de código como entrada.Isso poderia funcionar com um código de idioma apropriado se você usasse
char32_t
apenas:U'\U0001F34C'
é uma única unidade de código no UTF-32.No entanto, isso ainda significa que você só obtém as transformações simples de maiúsculas
toupper
e minúsculas etolower
que, por exemplo, não são boas o suficiente para algumas localidades alemãs: "ß" maiúsculas para "SS" ☦, mastoupper
pode retornar apenas uma unidade de código decaracteres.Em seguida,
wstring_convert
/wbuffer_convert
e as facetas de conversão de código padrão.wstring_convert
é usado para converter entre strings em uma determinada codificação em strings em outra determinada codificação. Existem dois tipos de sequência envolvidos nessa transformação, que o padrão chama de sequência de bytes e uma sequência ampla. Como esses termos são realmente enganosos, prefiro usar "serializado" e "desserializado", respectivamente, em vez disso †.As codificações a serem convertidas são decididas por um codecvt (uma faceta de conversão de código) passado como um argumento de tipo de modelo para
wstring_convert
.wbuffer_convert
executa uma função semelhante, mas como umamplobuffer de fluxo desserializado que envolve um buffer de fluxo serializado debytes. Qualquer E / S é executada através do buffer de fluxo serializado debytessubjacente com conversões de e para as codificações fornecidas pelo argumento codecvt. Escrever serializa nesse buffer e depois grava a partir dele, e a leitura lê no buffer e desserializa a partir dele.A norma fornece alguns modelos de classe codecvt para uso com essas facilidades:
codecvt_utf8
,codecvt_utf16
,codecvt_utf8_utf16
, e algumascodecvt
especializações. Juntas, essas facetas padrão fornecem todas as seguintes conversões. (Nota: na lista a seguir, a codificação à esquerda é sempre a sequência serializada / streambuf e a codificação à direita é sempre a sequência desserializada / streambuf; o padrão permite conversões em ambas as direções).codecvt_utf8<char16_t>
ecodecvt_utf8<wchar_t>
ondesizeof(wchar_t) == 2
;codecvt_utf8<char32_t>
,codecvt<char32_t, char, mbstate_t>
ecodecvt_utf8<wchar_t>
em quesizeof(wchar_t) == 4
;codecvt_utf16<char16_t>
ecodecvt_utf16<wchar_t>
ondesizeof(wchar_t) == 2
;codecvt_utf16<char32_t>
ecodecvt_utf16<wchar_t>
ondesizeof(wchar_t) == 4
;codecvt_utf8_utf16<char16_t>
,codecvt<char16_t, char, mbstate_t>
ecodecvt_utf8_utf16<wchar_t>
ondesizeof(wchar_t) == 2
;codecvt<wchar_t, char_t, mbstate_t>
codecvt<char, char, mbstate_t>
.Vários deles são úteis, mas há muitas coisas estranhas aqui.
Primeiro: santo alto substituto! esse esquema de nomeação é confuso.
Então, há muito suporte ao UCS-2. O UCS-2 é uma codificação do Unicode 1.0 que foi substituída em 1996 porque suporta apenas o plano multilíngue básico. Por que o comitê achou desejável se concentrar em uma codificação que foi substituída há mais de 20 anos, eu não sei ‡. Não é como se o suporte para mais codificações fosse ruim ou algo assim, mas o UCS-2 aparece com muita frequência aqui.
Eu diria que isso
char16_t
é obviamente destinado ao armazenamento de unidades de código UTF-16. No entanto, essa é uma parte do padrão que pensa o contrário.codecvt_utf8<char16_t>
não tem nada a ver com UTF-16. Por exemplo,wstring_convert<codecvt_utf8<char16_t>>().to_bytes(u"\U0001F34C")
compilará bem, mas falhará incondicionalmente: a entrada será tratada como a sequência UCS-2u"\xD83C\xDF4C"
, que não pode ser convertida em UTF-8 porque o UTF-8 não pode codificar nenhum valor no intervalo 0xD800-0xDFFF.Ainda na frente do UCS-2, não há como ler um fluxo de bytes UTF-16 em uma string UTF-16 com essas facetas. Se você tiver uma sequência de bytes UTF-16, não poderá desserializá-la em uma sequência de
char16_t
. Isso é surpreendente, porque é mais ou menos uma conversão de identidade. Ainda mais surpreendente, porém, é o fato de que há suporte para desserialização de um fluxo UTF-16 para uma cadeia UCS-2 comcodecvt_utf16<char16_t>
, o que na verdade é uma conversão com perdas.Porém, o suporte a UTF-16-as-bytes é bastante bom: suporta a detecção de endianess de uma BOM ou a seleção explícita no código. Ele também suporta a produção de saída com e sem uma lista técnica.
Existem algumas possibilidades de conversão mais interessantes ausentes. Não há como desserializar de um fluxo ou sequência de bytes UTF-16 para uma sequência UTF-8, pois o UTF-8 nunca é suportado como a forma desserializada.
E aqui o mundo estreito / amplo é completamente separado do mundo UTF / UCS. Não há conversões entre as codificações estreitas / largas no estilo antigo e as codificações Unicode.
Biblioteca de entrada / saída
A biblioteca de E / S pode ser usada para ler e escrever texto em codificações Unicode usando os recursos
wstring_convert
ewbuffer_convert
descritos acima. Eu não acho que há muito mais que precisaria ser suportado por essa parte da biblioteca padrão.Biblioteca de expressões regulares
Eu expus problemas com regexes C ++ e Unicode no estouro de pilha antes. Não repetirei todos esses pontos aqui, mas apenas declaro que os regexes C ++ não têm suporte Unicode de nível 1, que é o mínimo necessário para torná-los utilizáveis sem recorrer ao uso de UTF-32 em todos os lugares.
Sim é isso. Essa é a funcionalidade existente. Há muitas funcionalidades Unicode que não são vistas em nenhum lugar como algoritmos de normalização ou segmentação de texto.
Os suspeitos do costume: UTI e Boost.Locale .
† Uma sequência de bytes é, sem surpresa, uma sequência de bytes, ou seja,
char
objetos. No entanto, diferentemente de uma literal de cadeia ampla , que é sempre uma matriz dewchar_t
objetos, uma "cadeia ampla" nesse contexto não é necessariamente uma cadeia dewchar_t
objetos. De fato, o padrão nunca define explicitamente o que significa uma "cadeia ampla"; portanto, devemos adivinhar o significado do uso. Como a terminologia padrão é desleixada e confusa, eu uso a minha em nome da clareza.Codificações como UTF-16 podem ser armazenadas como sequências de
char16_t
, as quais não possuem endianness; ou eles podem ser armazenados como sequências de bytes, que possuem endianness (cada par consecutivo de bytes pode representar umchar16_t
valor diferente, dependendo da endianness). O padrão suporta esses dois formulários. Uma sequência dechar16_t
é mais útil para manipulação interna no programa. Uma sequência de bytes é a maneira de trocar essas strings com o mundo externo. Os termos que vou usar em vez de "byte" e "wide" são, portanto, "serializados" e "desserializados".‡ Se você está prestes a dizer "mas Windows!" segure seu 🐎🐎 . Todas as versões do Windows desde o Windows 2000 usam UTF-16.
☦ Sim, eu conheço as gromas Eszett (ẞ), mas mesmo que você altere todos os locais alemães da noite para o dia para ter maiúsculas em ẞ, ainda existem muitos outros casos em que isso falharia. Tente digitar em maiúsculas U + FB00 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟɪɢᴀᴛᴜʀᴇ ғғ. Não há ғғ ᴄᴀᴘɪᴛᴀʟ ʟɪɢᴀᴛᴜʀᴇ ғғ; apenas maiúsculas para dois Fs. Ou U + 01F0 ʟᴀᴛɪɴ sᴍᴀʟʟ ʟᴇᴛᴛᴇʀ ᴊ ᴡɪᴛʜ ᴄᴀʀᴏɴ; não há capital pré-composto; apenas em maiúsculas para uma maiúscula J e uma combinação de caracteres.
fonte
O Unicode não é suportado pela Biblioteca Padrão (para qualquer significado razoável de suportado).
std::string
não é melhor do questd::vector<char>
: é completamente alheio ao Unicode (ou qualquer outra representação / codificação) e simplesmente trata seu conteúdo como uma bolha de bytes.Se você só precisa armazenar e gerar blobs , funciona muito bem; mas assim que desejar a funcionalidade Unicode (número de pontos de código , número de grafemas etc.), você estará sem sorte.
A única biblioteca abrangente que conheço é a UTI . A interface C ++ foi derivada da Java, portanto, está longe de ser idiomática.
fonte
Você pode armazenar UTF-8 com segurança em um
std::string
(ou em umchar[]
ouchar*
, nesse caso), devido ao fato de que um NUL Unicode (U + 0000) é um byte nulo no UTF-8 e que essa é a única maneira de um nulo. byte pode ocorrer em UTF-8. Portanto, suas cadeias UTF-8 serão finalizadas corretamente de acordo com todas as funções de cadeia C e C ++, e você poderá usá-las com iostreams C ++ (incluindostd::cout
estd::cerr
, desde que seu código de idioma seja UTF-8).O que você não pode fazer com o
std::string
UTF-8 é obter comprimento em pontos de código.std::string::size()
informará o comprimento da string em bytes , que é apenas igual ao número de pontos de código quando você estiver no subconjunto ASCII do UTF-8.Se você precisar operar em cadeias UTF-8 no nível do ponto de código (ou seja, não apenas armazená-las e imprimi-las) ou se estiver lidando com a UTF-16, que provavelmente possui muitos bytes nulos internos, é necessário examinar os tipos de cadeia de caracteres largos.
fonte
std::string
pode ser lançado em iostreams com nulos incorporados.c_str()
, porquesize()
ainda funciona. Apenas APIs quebradas (ou seja, aquelas que não conseguem lidar com nulos incorporados, como a maioria do mundo C) são interrompidas.c_str()
porquec_str()
é suposto retornar os dados como uma cadeia C terminada em nulo - o que é impossível, devido ao fato de que as cadeias C não podem ter nulos incorporados.c_str()
agora simplesmente retorna o mesmo quedata()
, ou seja, tudo isso. APIs que assumem um tamanho podem consumi-lo. APIs que não, não podem.c_str()
garante que o resultado seja seguido por um objeto NUL do tipo char, e acho quedata()
não. Não, parece quedata()
agora faz isso também. (Claro, isso não é necessário para a APIs que consomem o tamanho, em vez de inferir que a partir de uma pesquisa terminator)O C ++ 11 possui alguns novos tipos de cadeias literais para Unicode.
Infelizmente, o suporte na biblioteca padrão para codificações não uniformes (como UTF-8) ainda é ruim. Por exemplo, não há uma maneira agradável de obter o comprimento (em pontos de código) de uma string UTF-8.
fonte
std::string
pode conter uma string UTF-8 sem problemas, mas, por exemplo, olength
método retorna o número de bytes na string e não o número de pontos de código.ñ
como 'LETRA PEQUENA LATINA N COM TILDE' (U + 00F1) (que é um ponto de código) ou 'LETRA PEQUENA LATINA N' ( U + 006E) seguido de 'COMBINING TILDE' (U + 0303), que é dois pontos de código.LATIN SMALL LETTER N'
==(U+006E) followed by 'COMBINING TILDE' (U+0303)
.No entanto, existe uma biblioteca bastante útil chamada tiny-utf8 , que é basicamente um substituto para
std::string
/std::wstring
. Ele visa preencher a lacuna da classe de contêiner utf8-string ainda ausente.Essa pode ser a maneira mais confortável de 'lidar' com strings utf8 (ou seja, sem normalização unicode e coisas semelhantes). Você opera confortavelmente em pontos de código , enquanto sua string permanece codificada em s codificados em comprimento de execução
char
.fonte