programação C: Como programar para Unicode?

votos
73

O pré-requisitos são necessários para fazer rigorosa programação Unicode?

Será que isso significa que meu código não deve usar chartipos em qualquer lugar e que as funções precisam ser usado que podem lidar com wint_te wchar_t?

E qual é o papel desempenhado por sequências de caracteres de vários bytes nesse cenário?

Publicado 08/02/2009 em 22:22
fonte usuário
Em outras línguas...                            


8 respostas

votos
36

C99 ou mais cedo

O padrão C (C99) prevê caracteres largos e caracteres multi-byte, mas desde que não há nenhuma garantia sobre o que esses caracteres largos pode segurar, seu valor é um pouco limitado. Para uma determinada aplicação, eles fornecem suporte útil, mas se o seu código deve ser capaz de mover-se entre implementações, há insuficiente garantia de que eles serão úteis.

Consequentemente, a abordagem sugerida por Hans van Eck (que é para escrever um invólucro em torno do ICU - International Components for Unicode - biblioteca) é som, IMO.

A codificação UTF-8 tem muitos méritos, uma das quais é que se você não mexer com os dados (truncando-lo, por exemplo), então ele pode ser copiado por funções que não estão plenamente conscientes das complexidades de UTF-8 codificação. Este não é categoricamente o caso comwchar_t .

Unicode na íntegra é um formato de 21 bits. Ou seja, Unicode reserva-se pontos de código de U + 0000 a U + 10FFFF.

Uma das coisas úteis sobre o UTF-8, UTF-16 e formatos UTF-32 (onde UTF defende Unicode Transformation Format - ver Unicode ) é que você pode converter entre os três representações sem perda de informação. Cada um pode representar qualquer coisa que os outros podem representar. Ambos UTF-8 e UTF-16 são formatos multi-byte.

UTF-8 é bem conhecido por ser um formato de multi-byte, com uma estrutura de cuidado que torna possível para encontrar o início de caracteres em uma string de forma confiável, a partir de qualquer ponto da string. caracteres de byte único tem o alto-bit definido para zero. caracteres multi-byte tem o primeiro carácter de início com um dos padrões de bits 110, 1110 ou 11110 (por dois bytes, 3-byte ou 4 bytes caracteres), com os bytes subsequentes, começando sempre 10. Os caracteres de continuação estão sempre na gama 0x80 .. 0xBF. Existem regras que UTF-8 caracteres devem ser representados no formato mínimo possível. Uma consequência destas regras é que os bytes 0xC0 e 0xC1 (também 0xF5..0xFF) não pode aparecer em dados UTF-8 válidos.

 U+0000 ..   U+007F  1 byte   0xxx xxxx
 U+0080 ..   U+07FF  2 bytes  110x xxxx   10xx xxxx
 U+0800 ..   U+FFFF  3 bytes  1110 xxxx   10xx xxxx   10xx xxxx
U+10000 .. U+10FFFF  4 bytes  1111 0xxx   10xx xxxx   10xx xxxx   10xx xxxx

Originalmente, esperava-se que Unicode seria um conjunto de códigos de 16 bits e tudo iria caber em um espaço de código de 16 bits. Infelizmente, o mundo real é mais complexa, e teve que ser expandida para a codificação de 21-bit.

UTF-16 é, assim, de uma única unidade (palavra de 16 bits) para o conjunto de códigos 'básica multilingues Plane', o que significa que os caracteres com pontos de código de Unicode L + 0000 .. U + FFFF, mas utiliza duas unidades (32 bits) para caracteres fora desta gama. Assim, o código que funciona com a codificação UTF-16 deve ser capaz de lidar com largura variável codificações, como UTF-8 obrigação. Os códigos para os caracteres de dois unidade são chamados substitutos.

Substitutos são pontos de código a partir de duas gamas de valores especiais Unicode, reservados para uso como principais, e que arrastam valores de unidades de código emparelhados em UTF-16. Guiar, também chamado alta, substitutos são de L + D800 a U + DBFF, e arrastando, ou baixa, são substitutos de L + DC00 a U + DFFF. Eles são chamados substitutos, já que eles não representam personagens diretamente, mas apenas como um par.

UTF-32, é claro, pode codificar qualquer ponto de código Unicode em uma única unidade de armazenamento. É eficiente para a computação, mas não para o armazenamento.

Você pode encontrar muito mais informações na UTI e sites Unicode.

C11 e <uchar.h>

O padrão C11 mudou as regras, mas nem todas as implementações têm apanhados com as mudanças, mesmo agora (meados de 2017). O padrão C11 resume as alterações de apoio Unicode como:

  • Unicode caracteres e cadeias de caracteres ( <uchar.h>) (originalmente especificado na norma ISO / IEC TR 19769: 2004)

O que se segue é um esboço mínimo nua da funcionalidade. A especificação inclui:

6.4.3 nomes de personagens universais

Sintaxe
universal caracteres-name:
    \u hex-quad
    \U hex-quad hex-quad
hex-quad:
    hexadecimal dígitos hexadecimal dígitos hexadecimal dígitos hexadecimal dígitos

7,28 utilitários Unicode <uchar.h>

O cabeçalho <uchar.h>declara tipos e funções para a manipulação de caracteres Unicode.

Os tipos são declarados mbstate_t(descrito em 7.29.1) e size_t(descrito em 7,19);

char16_t

que é um tipo inteiro sem sinal utilizado para caracteres de 16 bits e é do mesmo tipo que uint_least16_t(descrito em 7.20.1.2); e

char32_t

que é um tipo inteiro sem sinal utilizado para caracteres de 32 bits e é do mesmo tipo que uint_least32_t(também descrito no 7.20.1.2).

(Traduzindo as referências cruzadas: <stddef.h>define size_t, <wchar.h>define mbstate_t, e <stdint.h>define uint_least16_te uint_least32_t.) O <uchar.h>cabeçalho também define um conjunto mínimo de funções de conversão (reiniciáveis):

  • mbrtoc16()
  • c16rtomb()
  • mbrtoc32()
  • c32rtomb()

Existem regras sobre quais caracteres Unicode podem ser usados em identificadores usando o \unnnnou \U00nnnnnnanotações. Você pode ter que ativar ativamente o apoio a tais personagens de identificadores. Por exemplo, GCC exige -fextended-identifierspara permitir que estes em identificadores.

Note-se que o MacOS Sierra (10.12.5), para citar apenas uma plataforma, não suporta <uchar.h>.

Respondeu 09/02/2009 em 08:00
fonte usuário

votos
20

Note-se que não se trata de "programação estrita unicode" per se, mas alguma experiência prática.

O que fizemos na minha empresa era criar uma biblioteca invólucro em torno da biblioteca ICU da IBM. A biblioteca wrapper tem uma interface UTF-8 e converte para UTF-16 quando é necessário chamar UTI. No nosso caso, nós não se preocupar muito com sucessos de desempenho. Quando o desempenho foi um problema, nós também fornecido interfaces de UTF-16 (usando o nosso próprio tipo de dados).

Aplicações poderia permanecer em grande parte como-é (usando caracteres), embora em alguns casos, eles precisam estar cientes de certas questões. Por exemplo, em vez de strncpy () usamos um invólucro que evita cortar sequências UTF-8. No nosso caso, isso é suficiente, mas também pode considerar cheques para combinar caracteres. Temos também wrappers para a contagem do número de codepoints, o número de grafemas, etc.

Quando a interface com outros sistemas, às vezes precisamos fazer composição caráter personalizado, assim você pode precisar de alguma flexibilidade lá (dependendo do aplicativo).

Nós não usamos wchar_t. Usando UTI evita problemas inesperados na portabilidade (mas não outros problemas inesperados, é claro :-).

Respondeu 08/02/2009 em 23:44
fonte usuário

votos
9

Este FAQ é uma riqueza de informações. Entre essa página e este artigo de Joel Spolsky , você vai ter um bom começo.

Uma conclusão a que cheguei ao longo do caminho:

  • wchar_té de 16 bits no Windows, mas não necessariamente 16 bits em outras plataformas. Eu acho que é um mal necessário no Windows, mas provavelmente pode ser evitado em outros lugares. A razão é importante no Windows é que você precisa para usar os arquivos que possuem caracteres não-ASCII no nome (junto com a versão W de funções).

  • Note-se que as APIs do Windows que levam wchar_tcordas esperar UTF-16. Note também que isso é diferente de UCS-2. Tome nota dos pares substitutos. Esta página de teste tem provas esclarecedoras.

  • Se você está programando no Windows, você não pode usar fopen(), fread(), fwrite(), etc., uma vez que só pode tomar char *e não entendem codificação UTF-8. Faz portabilidade doloroso.

-DB

Respondeu 09/02/2009 em 17:34
fonte usuário

votos
7

Para fazer rigorosa programação Unicode:

  • Usar somente APIs de cordas que são Unicode ciente ( NÃO strlen , strcpy... mas os seus homólogos WideString wstrlen, wsstrcpy...)
  • Ao lidar com um bloco de texto, utilizar uma codificação que permite armazenar caracteres Unicode (UTF-7, UTF-8, UTF-16, UCS-2, ...) sem perda.
  • Verifique se o conjunto de caracteres padrão OS é compatível com Unicode (ex: utf-8)
  • Use fontes que são Unicode compatível (por exemplo arial_unicode)

Multi-byte sequências de caracteres é uma codificação que é anterior à codificação UTF-16 (o usado normalmente com wchar_t) e parece-me que é bastante somente para Windows.

Eu nunca ouvi falar wint_t.

Respondeu 08/02/2009 em 22:56
fonte usuário

votos
3

A coisa mais importante é sempre fazer uma clara distinção entre texto e dados binários . Tente seguir o modelo do Python 3.x strvs.bytes ou SQL TEXTvs. BLOB.

Infelizmente, C confunde o problema usando charpara tanto "de caracteres ASCII" e int_least8_t. Você vai querer fazer algo como:

typedef char UTF8; // for code units of UTF-8 strings
typedef unsigned char BYTE; // for binary data

Você pode querer typedefs para UTF-16 e UTF-32 unidades de código também, mas isso é mais complicado porque a codificação wchar_tnão está definido. Você vai precisar de apenas um pré-processador #ifs. Algumas macros úteis em C e C ++ 0x são:

  • __STDC_UTF_16__- Se definido, o tipo _Char16_texiste e é UTF-16.
  • __STDC_UTF_32__- Se definido, o tipo _Char32_texiste e é UTF-32.
  • __STDC_ISO_10646__- Se define, em seguida, wchar_té UTF-32.
  • _WIN32- No Windows, wchar_té UTF-16, mesmo que isso quebra o padrão.
  • WCHAR_MAX- Pode ser usado para determinar o tamanho da wchar_t, mas não se o sistema operacional usa para representar Unicode.

Será que isso significa que meu código não deve usar tipos de char em qualquer lugar e que as funções precisam ser usado que podem lidar com wint_t e wchar_t?

Veja também:

Não UTF-8 é uma codificação Unicode perfeitamente válido que utiliza char*cordas. Ele tem a vantagem de que se o seu programa é transparente para bytes não ASCII (por exemplo, um conversor de fim de linha que atua sobre \re \nmas passa por outros personagens inalteradas), você vai precisar fazer nenhuma mudança em tudo!

Se você vai com UTF-8, você vai precisar alterar todos os pressupostos que char= caracteres (por exemplo, não chame toupperem um loop) ou char= coluna de tela (por exemplo, por quebra de texto).

Se você vai com UTF-32, você vai ter a simplicidade de caracteres de largura fixa (mas não de largura fixa grafemas , mas terá de mudar o tipo de todas as suas cordas).

Se você vai com UTF-16, você terá que descartar tanto a suposição de caracteres de largura fixa e a assunção de unidades de código de 8 bits, o que torna este o mais difícil caminho de atualização do codificações de byte único.

Eu recomendaria ativamente evitando wchar_t porque não é multi-plataforma: Às vezes é UTF-32, às vezes é UTF-16, e às vezes é uma codificação pré-Unicode do Leste Asiático. Eu recomendo usartypedefs

Ainda mais importante, evitarTCHAR .

Respondeu 18/08/2010 em 14:45
fonte usuário

votos
2

Eu não confiar em qualquer implementação da biblioteca padrão. Basta rolar seus próprios tipos de unicode.

#include <windows.h>

typedef unsigned char utf8_t;
typedef unsigned short utf16_t;
typedef unsigned long utf32_t;

int main ( int argc, char *argv[] )
{
  int msgBoxId;
  utf16_t lpText[] = { 0x03B1, 0x0009, 0x03B2, 0x0009, 0x03B3, 0x0009, 0x03B4, 0x0000 };
  utf16_t lpCaption[] = L"Greek Characters";
  unsigned int uType = MB_OK;
  msgBoxId = MessageBoxW( NULL, lpText, lpCaption, uType );
  return 0;
}
Respondeu 29/03/2017 em 18:45
fonte usuário

votos
2

Você basicamente quer lidar com strings na memória como matrizes wchar_t em vez de carvão. Quando você faz qualquer tipo de I / O (como leitura / escrita de arquivos), você pode codificar / decodificar usando UTF-8 (esta é provavelmente a codificação mais comum), que é bastante simples de implementar. Apenas google as RFCs. Então in-memory nada deve ser multi-byte. Um wchar_t representa um caractere. Quando você vem para a serialização no entanto, que é quando você precisa codificar para algo como UTF-8, onde alguns personagens são representados por vários bytes.

Você também terá que escrever novas versões do strcmp etc para as cadeias de caracteres largos, mas isso não é um grande problema. O maior problema será interoperabilidade com bibliotecas / código existente que só aceitam matrizes de caracteres.

E quando se trata de sizeof (wchar_t) (você vai precisar 4 bytes se você quiser fazer isso direito) você sempre pode redefini-lo para um tamanho maior com typedef / hacks macro se você precisa.

Respondeu 09/02/2009 em 07:40
fonte usuário

votos
1

Pelo que eu sei, wchar_t é dependente de implementação (como pode ser visto a partir deste artigo wiki ). E não é unicode.

Respondeu 09/02/2009 em 07:03
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more