O UTF-16 deve ser considerado prejudicial?

432

Vou perguntar o que provavelmente é uma pergunta bastante controversa: "Uma das codificações mais populares, UTF-16, deve ser considerada prejudicial?"

Por que faço essa pergunta?

Quantos programadores estão cientes do fato de que o UTF-16 é realmente uma codificação de comprimento variável? Com isso, quero dizer que existem pontos de código que, representados como pares substitutos, levam mais de um elemento.

Eu sei; muitos aplicativos, estruturas e APIs usam UTF-16, como String de Java, String de C #, APIs Win32, bibliotecas Qt GUI, biblioteca ICU Unicode etc. No entanto, com tudo isso, existem muitos bugs básicos no processamento de caracteres fora do BMP (caracteres que devem ser codificados usando dois elementos UTF-16).

Por exemplo, tente editar um desses caracteres:

Você pode perder alguns, dependendo das fontes instaladas. Esses caracteres estão todos fora do BMP (Basic Multilingual Plane). Se você não conseguir ver esses caracteres, tente também examiná-los na referência de Caracteres Unicode .

Por exemplo, tente criar nomes de arquivos no Windows que incluam esses caracteres; tente excluir esses caracteres com um "backspace" para ver como eles se comportam em diferentes aplicativos que usam UTF-16. Eu fiz alguns testes e os resultados são muito ruins:

  • O Opera tem problemas com a edição (exclua 2 pressionamentos necessários no backspace)
  • O bloco de notas não pode lidar com eles corretamente (exclua 2 pressionamentos necessários no backspace)
  • Edição de nomes de arquivo nas caixas de diálogo da Janela quebradas (excluir 2 pressionadas necessárias no backspace)
  • Todos os aplicativos QT3 não podem lidar com eles - mostram dois quadrados vazios em vez de um símbolo.
  • O Python codifica esses caracteres incorretamente quando usado diretamente u'X'!=unicode('X','utf-16')em algumas plataformas quando o caractere X está fora do BMP.
  • O unicodedata do Python 2.5 falha ao obter propriedades desses caracteres quando o python é compilado com seqüências de caracteres Unicode UTF-16.
  • O StackOverflow parece remover esses caracteres do texto se editado diretamente como caracteres Unicode (esses caracteres são mostrados usando escapes Unicode HTML).
  • O WinForms TextBox pode gerar uma seqüência de caracteres inválida quando limitada ao MaxLength.

Parece que esses erros são extremamente fáceis de encontrar em muitos aplicativos que usam UTF-16.

Então ... Você acha que o UTF-16 deve ser considerado prejudicial?

Artyom
fonte
64
Não é realmente correto. Eu explico, se você escrever "שָׁ" o caractere composto que consiste em "ש", "ָ" e "ׁ", vovels, a remoção de cada uma delas é lógica, você removerá um ponto de código ao pressionar " backspace "e remova todos os caracteres, incluindo vovels, quando pressionar" del ". Mas você nunca produz um estado ilegal de texto - pontos de código ilegais. Portanto, a situação quando você pressiona backspace e obtém texto ilegato está incorreta.
41
CiscoIPPhone: Se um bug é "relatado várias vezes por pessoas diferentes" e, dois anos depois, um desenvolvedor escreve em um blog de desenvolvimento que "Acredite ou não, o comportamento é principalmente intencional!", Então (para colocar levemente) Eu costumo pensar que provavelmente não é a melhor decisão de design já feita. :-) Só porque é intencional, não significa que não seja um bug.
145
Ótimo post. O UTF-16 é realmente o "pior dos dois mundos": o UTF8 é de tamanho variável, abrange todo o Unicode, requer um algoritmo de transformação para e de pontos de código brutos, restringe ao ASCII e não possui problemas de endianidade. O UTF32 é de comprimento fixo, não requer transformação, mas ocupa mais espaço e possui problemas de endianidade. Até aí tudo bem, você pode usar UTF32 internamente e UTF8 para serialização. Mas o UTF16 não tem benefícios: é dependente de endian, tem comprimento variável, ocupa muito espaço, não é compatível com ASCII. O esforço necessário para lidar adequadamente com o UTF16 poderia ser melhor gasto com o UTF8.
Kerrek SB
26
@Ian: UTF-8 NÃO tem as mesmas ressalvas que UTF-8. Você não pode ter substitutos no UTF-8. O UTF-8 não se disfarça como algo que não é, mas a maioria dos programadores que usam o UTF-16 está usando errado. Eu sei. Eu os assisti de novo e de novo e de novo e de novo.
tchrist
18
Além disso, o UTF-8 não tem o problema, porque todo mundo o trata como uma codificação de largura variável. A razão pela qual o UTF-16 tem o problema é porque todo mundo o trata como uma codificação de largura fixa.
Christoffer Hammarström

Respostas:

340

Esta é uma resposta antiga.
Consulte UTF-8 em todos os lugares para obter as atualizações mais recentes.

Opinião: Sim, o UTF-16 deve ser considerado prejudicial . A razão pela qual existe é que, há algum tempo, costumava haver uma crença equivocada de que o widechar seria o que o UCS-4 agora é.

Apesar do "anglocentrismo" do UTF-8, ele deve ser considerado a única codificação útil para o texto. Pode-se argumentar que nunca deveriam existir códigos-fonte de programas, páginas da Web e arquivos XML, nomes de arquivos do SO e outras interfaces de texto de computador para computador. Mas quando o fazem, o texto não é apenas para leitores humanos.

Por outro lado, a sobrecarga UTF-8 é um preço baixo a ser pago, enquanto apresenta vantagens significativas. Vantagens como compatibilidade com código inconsciente que apenas passa strings com char*. Isso é ótimo. Existem poucos caracteres úteis que são MAIS CURTOS no UTF-16 do que no UTF-8.

Acredito que todas as outras codificações acabarão morrendo. Isso envolve que o MS-Windows, Java, ICU, python parem de usá-lo como favorito. Após longas pesquisas e discussões, as convenções de desenvolvimento da minha empresa proíbem o uso de UTF-16 em qualquer lugar, exceto nas chamadas da API do SO, e isso apesar da importância do desempenho em nossos aplicativos e do fato de usarmos o Windows. As funções de conversão foram desenvolvidas para converter UTF8s sempre assumidos em std::stringUTF-16 nativo, que o próprio Windows não suporta adequadamente .

Para as pessoas que dizem " usam o que é necessário onde é necessário ", digo: há uma enorme vantagem em usar a mesma codificação em todos os lugares e não vejo razão suficiente para fazer o contrário. Em particular, acho que adicionar wchar_tao C ++ foi um erro, assim como as adições Unicode ao C ++ 0x. O que deve ser exigido de implementações STL, porém, é que cada std::stringou char*parâmetro seria considerado unicode compatível.

Também sou contra a abordagem " use o que você quer ". Não vejo razão para tanta liberdade. Há confusão suficiente no assunto do texto, resultando em todo esse software danificado. Dito isto, estou convencido de que os programadores devem finalmente chegar a um consenso sobre o UTF-8 como uma maneira adequada. (Eu venho de um país que não fala ascii e cresci no Windows, então seria esperado que eu atacasse o UTF-16 com base em motivos religiosos).

Gostaria de compartilhar mais informações sobre como faço para escrever texto no Windows e o que recomendo a todos os demais para correção de unicode verificada em tempo de compilação, facilidade de uso e melhor plataforma do código. A sugestão difere substancialmente do que geralmente é recomendado como a maneira correta de usar o Unicode no Windows. No entanto, a pesquisa aprofundada dessas recomendações resultou na mesma conclusão. Então aqui vai:

  • Não use wchar_tou std::wstringem qualquer lugar que não seja o ponto adjacente às APIs que aceitam UTF-16.
  • Não use _T("")ou L""literais UTF-16 (estes devem ser retirados do padrão da IMO, como parte da reprovação de UTF-16).
  • Não use tipos, funções ou seus derivados sensíveis à _UNICODEconstante, como LPTSTRou CreateWindow().
  • No entanto, _UNICODEsempre definido, para evitar que as char*strings para o WinAPI sejam silenciosamente compiladas
  • std::stringse char*em qualquer lugar do programa são considerados UTF-8 (se não dito o contrário)
  • Todas as minhas strings são std::string, embora você possa passar char * ou literal para convert(const std::string &).
  • use apenas funções Win32 que aceitam widechars ( LPWSTR). Nunca aqueles que aceitam LPTSTRou LPSTR. Passe os parâmetros desta maneira:

    ::SetWindowTextW(Utils::convert(someStdString or "string litteral").c_str())
    

    (A política usa as funções de conversão abaixo.)

  • Com seqüências de caracteres MFC:

    CString someoneElse; // something that arrived from MFC. Converted as soon as possible, before passing any further away from the API call:
    
    std::string s = str(boost::format("Hello %s\n") % Convert(someoneElse));
    AfxMessageBox(MfcUtils::Convert(s), _T("Error"), MB_OK);
    
  • Trabalhando com arquivos, nomes de arquivos e fstream no Windows:

    • Nunca passe std::stringou const char*argumentos nome do arquivo para fstreama família. O MSVC STL não suporta argumentos UTF-8, mas possui uma extensão não padrão que deve ser usada da seguinte maneira:
    • Converta std::stringargumentos para std::wstringcom Utils::Convert:

      std::ifstream ifs(Utils::Convert("hello"),
                        std::ios_base::in |
                        std::ios_base::binary);
      

      Teremos que remover manualmente a conversão, quando a atitude da MSVC fstreammudar.

    • Este código não é multiplataforma e pode ter que ser alterado manualmente no futuro
    • Consulte o fstreamcaso de pesquisa / discussão unicode 4215 para obter mais informações.
    • Nunca produza arquivos de saída de texto com conteúdo não UTF8
    • Evite usar fopen()por razões RAII / OOD. Se necessário, use as _wfopen()convenções e WinAPI acima.

// For interface to win32 API functions
std::string convert(const std::wstring& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

std::wstring convert(const std::string& str, unsigned int codePage /*= CP_UTF8*/)
{
    // Ask me for implementation..
    ...
}

// Interface to MFC
std::string convert(const CString &mfcString)
{
#ifdef UNICODE
    return Utils::convert(std::wstring(mfcString.GetString()));
#else
    return mfcString.GetString();   // This branch is deprecated.
#endif
}

CString convert(const std::string &s)
{
#ifdef UNICODE
    return CString(Utils::convert(s).c_str());
#else
    Exceptions::Assert(false, "Unicode policy violation. See W569"); // This branch is deprecated as it does not support unicode
    return s.c_str();   
#endif
}
Pavel Radzivilovsky
fonte
39
Eu não posso concordar. As vantagens do utf16 sobre o utf8 para muitos idiomas asiáticos dominam completamente os pontos que você coloca. É ingênuo esperar que japoneses, tailandeses, chineses etc. desistam dessa codificação. Os conflitos problemáticos entre conjuntos de caracteres ocorrem quando os conjuntos de caracteres geralmente parecem semelhantes, exceto com diferenças. Sugiro padronizar: 7bit fixo: iso-irv-170; Variável de 8 bits: utf8; Variável de 16 bits: utf16; 32 bits corrigidos: ucs
82
@ Charles: obrigado pela sua contribuição. É verdade que alguns caracteres BMP são mais longos em UTF-8 do que em UTF-16. Mas, vamos ser sinceros: o problema não está nos bytes que os caracteres chineses BMP usam, mas na complexidade de design do software que surge. Se um programador chinês precisa projetar caracteres de tamanho variável de qualquer maneira, parece que o UTF-8 ainda é um preço baixo a pagar em comparação com outras variáveis ​​do sistema. Ele pode usar o UTF-16 como um algoritmo de compactação se o espaço for muito importante, mas mesmo assim não será páreo para o LZ, e após o LZ ou outra compactação genérica ambos terão o mesmo tamanho e entropia.
32
O que eu basicamente digo é que a simplificação oferecida pela codificação One também é compatível com os programas char * existentes e também é a mais popular hoje em dia, pois tudo é inimaginável. É quase como nos bons e velhos dias de "texto simples". Deseja abrir um arquivo com um nome? Não há necessidade de se preocupar com o tipo de unicode que você está fazendo, etc. etc. Sugiro que nós, desenvolvedores, confinemos o UTF-16 a casos muito especiais de otimização severa, onde um pouquinho de desempenho vale a pena por meses de trabalho.
17
O Linux teve um requisito específico ao optar por usar o UTF-8 internamente: compatibilidade com o Unix. O Windows não precisava disso e, assim, quando os desenvolvedores implementaram o Unicode, eles adicionaram versões UCS-2 de quase todas as funções que manipulam texto e fizeram com que os multibytes simplesmente se convertessem no UCS-2 e chamassem os outros. Depois, eles substituem o UCS-2 pelo UTF-16. O Linux, por outro lado, manteve codificações de 8 bits e, portanto, usou UTF-8, pois é a escolha apropriada nesse caso.
Mircea Chirea
34
@Pavel Radzivilovsky: Aliás, seus escritos sobre "Eu acredito que todas as outras codificações acabarão morrendo. Isso envolve que MS-Windows, Java, ICU, python parem de usá-lo como favorito". e "Em particular, acho que adicionar wchar_t ao C ++ foi um erro, e também as adições unicode ao C ++ Ox". são bastante ingênuos ou muito, muito arrogantes. E isso vem de alguém que codifica em casa um Linux e que está feliz com os caracteres UTF-8. Para ser franco: não vai acontecer .
paercebal
157

Pontos de código Unicode não são caracteres! Às vezes, eles nem são glifos (formas visuais).

Alguns exemplos:

  • Pontos de código de algarismos romanos como "ⅲ". (Um único caractere que se parece com "iii".)
  • Caracteres acentuados como "á", que podem ser representados como um único caractere combinado "\ u00e1" ou um caractere e diacrítico separado "\ u0061 \ u0301".
  • Caracteres como sigma minúsculo grego, que têm formas diferentes para as posições do meio ("σ") e final ("ς") das palavras, mas que devem ser consideradas sinônimos para pesquisa.
  • Hífen discricionário Unicode U + 00AD, que pode ou não ser exibido visualmente, dependendo do contexto, e que é ignorado na pesquisa semântica.

As únicas maneiras de acertar a edição Unicode é usar uma biblioteca escrita por um especialista ou tornar-se um especialista e escrever você mesmo. Se você está apenas contando pontos de código, está vivendo em um estado de pecado.

Daniel Newby
fonte
19
Este. Muito isso. O UTF-16 pode causar problemas, mas mesmo o uso do UTF-32 pode (e vai) ainda causar problemas.
BCAT
11
O que é um personagem? Você pode definir um ponto de código como um caractere e se sair bem. Se você quer dizer um glifo visível ao usuário, isso é outra coisa.
tchrist
7
@ tchrist certo para alocar espaço essa definição é boa, mas para mais alguma coisa? Não muito. Se você manipular um caractere combinado como um caractere único (ou seja, para uma operação de exclusão ou "obtenha o primeiro N caracteres"), você terá um comportamento estranho e errado. Se um ponto de código tiver apenas significado quando combinado com pelo menos outro, você não poderá lidar com ele sozinho de maneira sensata.
Voo
6
@ Pacerier, isso é tarde para a festa, mas eu tenho que comentar sobre isso. Algumas línguas têm conjuntos muito grandes de combinações potenciais de diacríticos (cf. vietnamita, isto é, mệt đừ). Ter combinações em vez de um caractere por diacrítico é muito útil.
asthasr
21
uma pequena nota sobre terminologia: codepoints não correspondem aos caracteres Unicode ; o que Daniel está falando aqui são personagens percebida pelo usuário , que correspondem a unicode aglomerados grafema
Christoph
54

Existe uma regra simples sobre qual UTF (Unicode Transformation Form) usar: - utf-8 para armazenamento e comunicação - utf-16 para processamento de dados - você pode usar o utf-32 se a maior parte da API da plataforma usada for utf-32 (comum no mundo UNIX).

Atualmente, a maioria dos sistemas usa utf-16 (Windows, Mac OS, Java, .NET, ICU, Qt). Consulte também este documento: http://unicode.org/notes/tn12/

De volta a "UTF-16 como prejudicial", eu diria: definitivamente não.

As pessoas que têm medo de substitutos (pensando que transformam Unicode em uma codificação de comprimento variável) não entendem as outras complexidades (muito maiores) que tornam o mapeamento entre caracteres e um ponto de código Unicode muito complexo: combinando caracteres, ligaduras, seletores de variação , caracteres de controle etc.

Basta ler esta série aqui http://www.siao2.com/2009/06/29/9800913.aspx e ver como o UTF-16 se torna um problema fácil.

Mihai Nita
fonte
26
Por favor, adicione alguns exemplos em que o UTF-32 é comum no mundo UNIX!
maxschlepzig
48
Não, você não deseja usar o UTF-16 para processamento de dados. É um pé no saco. Tem todas as desvantagens do UTF-8, mas nenhuma de suas vantagens. O UTF-8 e o UTF-32 são claramente superiores ao hack vicioso anteriormente conhecido como Mrs UTF-16, cujo nome de solteira era UCS-2.
tchrist
34
Ontem, encontrei um bug no equalsIgnoreCasemétodo da classe String principal do Java (também outros na classe string) que nunca estaria lá se o Java tivesse usado UTF-8 ou UTF-32. Existem milhões dessas explosões adormecidas em qualquer código que use UTF-16, e eu estou cansado e cansado delas. O UTF-16 é uma varíola cruel que assola nosso software com bugs insidiosos para todo o sempre. É claramente prejudicial e deve ser preterido e banido.
tchrist
7
@tchrist Wow, então, uma função consciente que não é de aluguel (porque foi escrita quando não havia nenhuma e está tristemente documentada de uma maneira que torna provavelmente impossível a adaptação - ela especifica .toUpperCase (char)) resultará no comportamento errado? Você está ciente de que uma função UTF-32 com um mapa de ponto de código desatualizado não lidaria com isso melhor? Além disso, toda a API Java lida com substitutos não muito bem e os pontos mais intricados sobre Unicode nem um pouco - e, posteriormente, a codificação usada não importaria nada.
Voo
8
-1: um incondicional .Substring(1)no .NET é um exemplo trivial de algo que interrompe o suporte a todos os Unicode não-BMP. Tudo o que usa UTF-16 tem esse problema; é muito fácil tratá-lo como uma codificação de largura fixa e você vê problemas muito raramente. Isso a torna uma codificação ativamente prejudicial se você deseja oferecer suporte ao Unicode.
Roman Starkov
43

Sim absolutamente.

Por quê? Tem a ver com o exercício do código .

Se você olhar essas estatísticas de uso de ponto de código em um corpus grande de Tom Christiansen, verá que os pontos de código BMP trans-8 bits são usados ​​em várias ordens, se a magnitude for maior que os pontos de código não BMP:

 2663710 U+002013 ‹–›  GC=Pd    EN DASH
 1065594 U+0000A0 ‹ ›  GC=Zs    NO-BREAK SPACE
 1009762 U+0000B1 ‹±›  GC=Sm    PLUS-MINUS SIGN
  784139 U+002212 ‹−›  GC=Sm    MINUS SIGN
  602377 U+002003 ‹ ›  GC=Zs    EM SPACE

 544 U+01D49E ‹𝒞›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL C
 450 U+01D4AF ‹𝒯›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL T
 385 U+01D4AE ‹𝒮›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL S
 292 U+01D49F ‹𝒟›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL D
 285 U+01D4B3 ‹𝒳›  GC=Lu    MATHEMATICAL SCRIPT CAPITAL X

Pegue o ditado TDD: "Código não testado é código quebrado" e reformule-o como "código não exercido é código quebrado" e pense na frequência com que os programadores precisam lidar com pontos de código não-BMP.

Erros relacionados ao não lidar com o UTF-16 como uma codificação de largura variável têm muito mais chances de passar despercebidos do que os erros equivalentes no UTF-8 . Algumas linguagens de programação ainda não garantem fornecer UTF-16 em vez de UCS-2, e algumas chamadas linguagens de programação de alto nível oferecem acesso a unidades de código em vez de pontos de código (mesmo C deve fornecer acesso a pontos de código se você usar wchar_t, independentemente do que algumas plataformas possam fazer).

ninjalj
fonte
16
"Erros relacionados ao não lidar com o UTF-16 como uma codificação de largura variável têm muito mais chances de passar despercebidos do que os erros equivalentes no UTF-8." Esse é o cerne da questão e, portanto, a resposta correta.
Sean McMillan
3
Precisamente. Se o seu manuseio UTF-8 for iniciado, será imediatamente óbvio. Se o seu manuseio UTF-8 for iniciado, você só notará se inserir caracteres Han ou símbolos matemáticos incomuns.
Caracol mecânico
1
É verdade, mas por outro lado, para que servem os testes de unidade se você depender da sorte para encontrar bugs em casos menos frequentes?
Musiphil
@musiphil: então, quando foi a última vez que você criou um teste de unidade para caracteres não BMP?
Ninjalj
1
Para elaborar minha declaração anterior: mesmo com o UTF-8, você não pode ter certeza de que cobriu todos os casos depois de ver apenas alguns exemplos de trabalho. O mesmo com o UTF-16: você precisa testar se o seu código funciona com não-substitutos e com substitutos. (Alguém poderia até argumentar que UTF-8 tem pelo menos quatro casos principais, enquanto UTF-16 tem apenas dois.)
musiphil
40

Eu sugeriria que pensar que o UTF-16 pode ser considerado prejudicial diz que você precisa entender melhor o unicode .

Desde que fui votado por apresentar minha opinião sobre uma questão subjetiva, deixe-me elaborar. O que exatamente incomoda você sobre o UTF-16? Você prefere se tudo foi codificado em UTF-8? UTF-7? Ou o UCS-4? Certamente, alguns aplicativos não são projetados para lidar com códigos de caracteres únicos, mas são necessários, especialmente no atual domínio da informação global de hoje, para a comunicação entre fronteiras internacionais.

Mas, realmente, se você acha que o UTF-16 deve ser considerado prejudicial, porque é confuso ou pode ser implementado de maneira inadequada (o unicode certamente pode ser), que método de codificação de caracteres seria considerado não prejudicial?

EDIT: Para esclarecer: Por que considerar implementações impróprias de um padrão um reflexo da qualidade do próprio padrão? Como outros observaram posteriormente, apenas porque um aplicativo usa uma ferramenta de forma inadequada, não significa que a própria ferramenta esteja com defeito. Se fosse esse o caso, provavelmente poderíamos dizer coisas como "palavra-chave var considerada prejudicial" ou "threading considerado prejudicial". Penso que a questão confunde a qualidade e a natureza do padrão com as dificuldades que muitos programadores têm para implementá-lo e usá-lo adequadamente, o que eu acho que decorre mais da falta de compreensão de como o unicode funciona, em vez do próprio unicode.

patjbs
fonte
33
-1: Que tal abordar algumas das objeções de Artyom, em vez de apenas ampará-lo?
8
BTW: Quando comecei a escrever este artigo, quase quis escrever "O artigo Joel on Softeare do Unicode deve ser considerado prejudicial" porque existem muitos erros. Por exemplo: a codificação utf-8 ocupa até 4 caracteres e não 6. Além disso, não distingue entre UCS-2 e UTF-16 que são realmente diferentes - e realmente causam os problemas de que falo.
32
Além disso, deve-se notar que, quando Joel escreveu esse artigo, o padrão UTF-8 era de 6 bytes, e não 4. O RFC 3629 mudou o padrão para 4 bytes vários meses depois que ele escreveu o artigo. Como quase tudo na internet, vale a pena ler de mais de uma fonte e estar ciente da idade de suas fontes. O link não pretendia ser o "fim de tudo ser tudo", mas um ponto de partida.
7
Gostaria de pic: utf-8 ou utf-32 que são: codificação de comprimento variável em quase todos os casos (incluindo BMP) ou codificação de comprimento fixo sempre.
18
@iconiK: Não seja bobo. O UTF-16 não é absolutamente o padrão de fato para o processamento de texto. Mostre-me uma linguagem de programação mais adequada ao processamento de texto que o Perl, que sempre (bem, por mais de uma década) usou caracteres abstratos com uma representação UTF-8 subjacente internamente. Por esse motivo, todo programa Perl manipula automaticamente todo o Unicode sem que o usuário precise constantemente procurar substitutos idiotas. O comprimento de uma string é sua contagem em pontos de código, não em unidades de código. Qualquer outra coisa é pura estupidez, colocando o retrocesso na compatibilidade retroativa.
tchrist
37

Não há nada errado com a codificação Utf-16. Mas os idiomas que tratam as unidades de 16 bits como caracteres provavelmente devem ser considerados mal projetados. Ter um tipo chamado ' char' que nem sempre representa um caractere é bastante confuso. Como a maioria dos desenvolvedores espera que um tipo de caractere represente um ponto ou caractere de código, muito código provavelmente será quebrado quando exposto a caracteres além do BMP.

Observe, no entanto, que mesmo usando utf-32 não significa que cada ponto de código de 32 bits sempre representará um caractere. Devido à combinação de caracteres, um caractere real pode consistir em vários pontos de código. Unicode nunca é trivial.

Entre. Provavelmente existe a mesma classe de bugs com plataformas e aplicativos que esperam que os caracteres sejam de 8 bits, alimentados com Utf-8.

JacquesB
fonte
12
No caso de Java, se você observar a linha do tempo ( java.com/en/javahistory/timeline.jsp ), verá que o desenvolvimento principal de String ocorreu enquanto o Unicode tinha 16 bits (foi alterado em 1996). Eles tiveram que apostar na capacidade de lidar com pontos de código não BMP, portanto, a confusão.
Kathy Van Stone
10
@ Kathy: Não é realmente uma desculpa para C #, no entanto. Geralmente, eu concordo que deveria haver um CodePointtipo, contendo um único ponto de código (21 bits), um CodeUnittipo, mantendo uma única unidade de código (16 bits para UTF-16) e um Charactertipo idealmente teria que suportar um grafema completo. Mas isso faz com que seja funcionalmente equivalente a um String...
Joey
1
Essa resposta tem quase dois anos, mas não posso deixar de comentar. "Ter um tipo chamado 'char' que nem sempre representa um personagem é bastante confuso." E, no entanto, as pessoas o usam o tempo todo em C e similares para representar dados inteiros que podem ser armazenados em um único byte.
JAB
E já vi muitos códigos C que não lidam com a codificação de caracteres corretamente.
dan04
1
O C # tem uma desculpa diferente: foi projetado para Windows e o Windows foi construído no UCS-2 (é muito irritante que ainda hoje as APIs do Windows não suportem UTF-8). Além disso, eu acho que a Microsoft queria compatibilidade Java (.NET 1.0 tinha uma biblioteca compatibilidade Java, mas abandonou o suporte Java muito rapidamente - eu estou supondo que este é devido à ação da Sun contra a MS?)
Qwertie
20

Minha escolha pessoal é sempre usar UTF-8. É o padrão no Linux para quase tudo. É compatível com muitos aplicativos herdados. Há uma sobrecarga muito mínima em termos de espaço extra usado para caracteres não latinos versus os outros formatos UTF, e há uma economia significativa de espaço para caracteres latinos. Na web, as línguas latinas reinam supremas, e acho que elas serão no futuro próximo. E para abordar um dos principais argumentos do post original: quase todo programador sabe que o UTF-8 às vezes terá caracteres de vários bytes. Nem todo mundo lida com isso corretamente, mas geralmente está ciente, o que é mais do que pode ser dito para o UTF-16. Mas, é claro, você precisa escolher o mais apropriado para sua aplicação. É por isso que há mais de um em primeiro lugar.

rmeador
fonte
3
O UTF-16 é mais simples para qualquer coisa dentro do BMP, é por isso que é usado tão amplamente. Mas também sou fã de UTF-8, também não tem problemas com a ordem de bytes, o que funciona em seu benefício.
26909 Malcolm
2
Teoricamente, sim. Na prática, existem coisas como, por exemplo, UTF-16BE, que significa UTF-16 em big endian sem BOM. Isso não é algo que eu inventei, é uma codificação real permitida nas tags ID3v2.4 (as tags ID3v2 são péssimas, mas são, infelizmente, amplamente usadas). E nesses casos, é necessário definir endianness externamente, porque o próprio texto não contém BOM. O UTF-8 é sempre escrito de uma maneira e não apresenta esse problema.
Malcolm
23
Não, o UTF-16 não é mais simples. É mais difícil. Isso engana e engana você, pensando que é de largura fixa. Todo esse código está quebrado e muito mais, porque você não percebe até que seja tarde demais. CASO EM PONTO: Acabei de encontrar outro bug UTF-16 estúpido nas bibliotecas principais do Java ontem, desta vez em String.equalsIgnoreCase, que foi deixado no bug do UCS-2, e, portanto, falha em 16/17 pontos de código Unicode válidos. Há quanto tempo esse código existe? Não há desculpa para que seja buggy. UTF-16 leva a pura estupidez e um acidente esperando para acontecer. Corra gritando de UTF-16.
tchrist
3
@tchrist É preciso ser um desenvolvedor muito ignorante para não saber que o UTF-16 não tem tamanho fixo. Se você começar com a Wikipedia, lerá o seguinte na parte superior: "Produz um resultado de tamanho variável de uma ou duas unidades de código de 16 bits por ponto de código". As perguntas frequentes sobre Unicode dizem o mesmo: unicode.org/faq//utf_bom.html#utf16-1 . Eu não sei, como o UTF-16 pode enganar alguém se estiver escrito em todo lugar que tem tamanho variável. Quanto ao método, ele nunca foi projetado para UTF-16 e não deve ser considerado Unicode, tão simples assim.
Malcolm
2
@tchrist Você tem uma fonte para suas estatísticas? Embora os bons programadores sejam escassos, acho que isso é bom, porque nos tornamos mais valiosos. :) Quanto às APIs Java, as peças baseadas em char podem ficar obsoletas, mas isso não garante que elas não serão usadas. E eles definitivamente não serão removidos por motivos de compatibilidade.
Malcolm
18

Bem, há uma codificação que usa símbolos de tamanho fixo. Eu certamente quero dizer UTF-32. Mas 4 bytes para cada símbolo são muito espaço desperdiçado, por que usá-lo em situações cotidianas?

Na minha opinião, a maioria dos problemas surge do fato de que alguns softwares ficaram atrás do padrão Unicode, mas não foram rápidos em corrigir a situação. Opera, Windows, Python, Qt - todos eles apareceram antes do UTF-16 se tornar amplamente conhecido ou até mesmo existir. Posso confirmar, porém, que no Opera, Windows Explorer e Bloco de Notas não há mais problemas com caracteres fora do BMP (pelo menos no meu PC). De qualquer forma, se os programas não reconhecem pares substitutos, eles não usam o UTF-16. Quaisquer que sejam os problemas que surgem ao lidar com esses programas, eles não têm nada a ver com o próprio UTF-16.

No entanto, acho que os problemas do software legado com apenas suporte a BMP são um pouco exagerados. Caracteres fora do BMP são encontrados apenas em casos e áreas muito específicos. De acordo com o FAQ oficial do Unicode , "mesmo no texto do leste asiático, a incidência de pares substitutos deve ser bem inferior a 1% de todo o armazenamento de texto em média". Obviamente, caracteres fora do BMP não devem ser negligenciados porque um programa não é compatível com Unicode, mas a maioria dos programas não se destina a trabalhar com textos que contenham esses caracteres. É por isso que se eles não o apóiam, é desagradável, mas não uma catástrofe.

Agora vamos considerar a alternativa. Se o UTF-16 não existisse, não teríamos uma codificação adequada para texto não ASCII, e todo o software criado para o UCS-2 teria que ser completamente reprojetado para permanecer compatível com Unicode. O último provavelmente retardaria apenas a adoção do Unicode. Também não teríamos sido capazes de manter a compabilidade com o texto no UCS-2 como o UTF-8 em relação ao ASCII.

Agora, deixando de lado todos os problemas herdados, quais são os argumentos contra a própria codificação? Eu realmente duvido que os desenvolvedores hoje em dia não saibam que o UTF-16 é de tamanho variável, está escrito em todos os lugares que estão na Wikipedia. O UTF-16 é muito menos difícil de analisar do que o UTF-8, se alguém apontar a complexidade como um possível problema. Também é errado pensar que é fácil atrapalhar a determinação do comprimento da string apenas no UTF-16. Se você usa UTF-8 ou UTF-32, ainda deve estar ciente de que um ponto de código Unicode não significa necessariamente um caractere. Fora isso, não acho que exista algo substancial contra a codificação.

Portanto, não acho que a codificação em si deva ser considerada prejudicial. O UTF-16 é um compromisso entre simplicidade e compacidade, e não há mal algum em usar o que é necessário onde for necessário . Em alguns casos, você precisa permanecer compatível com ASCII e precisa de UTF-8; em alguns casos, deseja trabalhar com ideogramas Han e economizar espaço usando UTF-16; em alguns casos, você precisa de representações universais de caracteres codificação de comprimento. Use o que é mais apropriado, apenas faça-o corretamente.

Malcolm
fonte
21
Essa é uma visão bastante antropocêntrica, Malcolm. Quase em pé de igualdade com "ASCII é bom o suficiente para os EUA - o resto do mundo deve se encaixar conosco".
27630 Jonathan Leffler
28
Na verdade, sou da Rússia e encontro cirílicos o tempo todo (incluindo meus próprios programas), então não acho que tenho uma visão anglo-centrada. :) Mencionar ASCII não é muito apropriado, porque não é Unicode e não suporta caracteres específicos. UTF-8, UTF-16, UTF-32 suportam os mesmos conjuntos de caracteres internacionais, eles apenas se destinam ao uso em suas áreas específicas. E este é exatamente o meu argumento: se você usa principalmente inglês, use UTF-8, se usa principalmente cirílicos, use UTF-16; se você usa idiomas antigos, use UTF-32. Bem simples.
26909 Malcolm
16
"Não é verdade, scripts asiáticos como japonês, chinês ou árabe também pertencem ao BMP. O BMP em si é realmente muito grande e certamente grande o suficiente para incluir todos os scripts usados ​​hoje em dia" Isso é tão errado. O BMP contém caracteres 0xFFFF (65536). Só o chinês tem mais do que isso. Os padrões chineses (GB 18030) têm mais do que isso. O Unicode 5.1 já alocou mais de 100.000 caracteres.
12
@ Marcolm: "O próprio BMP é realmente muito grande e certamente grande o suficiente para incluir todos os scripts usados ​​hoje em dia" Não é verdade. Neste ponto, o Unicode já alocou cerca de 100 mil caracteres, muito mais do que o BMP pode acomodar. Existem grandes pedaços de caracteres chineses fora do BMP. E alguns deles são exigidos pelo GB-18030 (padrão chinês obrigatório). Outros são exigidos pelos padrões japoneses e coreanos (não obrigatórios). Portanto, se você tentar vender algo nesses mercados, precisará além do suporte ao BMP.
8
Qualquer coisa que use UTF-16, mas possa lidar apenas com caracteres BMP estreitos, não está realmente usando UTF-16. Está com defeito e quebrado. A premissa do OP é sólida: o UTF-16 é prejudicial, porque leva pessoas ingênuas a escrever código quebrado. Você pode lidar com texto Unicode ou não. Se você não puder, estará escolhendo um subconjunto, que é tão estúpido quanto o processamento de texto somente ASCII.
tchrist
16

Anos de trabalho de internacionalização do Windows, especialmente em idiomas do Leste Asiático, podem ter me corrompido, mas eu me inclino para o UTF-16 para representações de strings internas ao programa e UTF-8 para armazenamento em rede ou arquivo de documentos semelhantes a texto sem formatação. No entanto, o UTF-16 geralmente pode ser processado mais rapidamente no Windows, então esse é o principal benefício do uso do UTF-16 no Windows.

Dar o salto para o UTF-16 melhorou drasticamente a adequação de produtos médios que manipulam textos internacionais. Existem apenas alguns casos estreitos em que os pares substitutos precisam ser considerados (exclusões, inserções e quebra de linha, basicamente) e o caso médio é geralmente uma passagem direta. E, diferentemente das codificações anteriores, como as variantes JIS, o UTF-16 limita os pares substitutos a uma faixa muito estreita; portanto, a verificação é realmente rápida e funciona para frente e para trás.

É verdade que também é rápido no UTF-8 codificado corretamente. Mas também existem muitos aplicativos UTF-8 quebrados que codificam incorretamente pares substitutos como duas sequências UTF-8. Portanto, o UTF-8 também não garante a salvação.

O IE lida com pares substitutos razoavelmente bem desde 2000, mais ou menos, mesmo que normalmente os esteja convertendo de páginas UTF-8 para uma representação interna UTF-16; Tenho certeza de que o Firefox também acertou, então não me importo com o que o Opera faz.

O UTF-32 (também conhecido como UCS4) é inútil para a maioria dos aplicativos, pois exige muito espaço, portanto é praticamente um iniciador.

JasonTrue
fonte
6
Não recebi seu comentário sobre os pares UTF-8 e substitutos. Pares substitutos é apenas um conceito que é significativo na codificação UTF-16, certo? Talvez o código que converte diretamente da codificação UTF-16 para codificação UTF-8 possa estar errado e, nesse caso, o problema esteja incorretamente lendo o UTF-16, não gravando o UTF-8. Isso está certo?
27715 Craig McQueen
11
O que Jason está falando é um software que implementa deliberadamente o UTF-8 dessa maneira: crie um par substituto e, em seguida, o UTF-8 codifique cada metade separadamente. O nome correto para essa codificação é CESU-8, mas o Oracle (por exemplo) o representa incorretamente como UTF-8. O Java emprega um esquema semelhante para serialização de objetos, mas está claramente documentado como "UTF-8 modificado" e apenas para uso interno. (Agora, se pudéssemos levar as pessoas a ler que a documentação e parar de usar DataInputStream # readUTF () e DataOutputStream # writeUTF () inapropriadamente ...)
AFAIK, UTF-32 ainda é uma codificação de comprimento variável e não é igual ao UCS4, que é um intervalo específico de ponto de código.
Eonil
@Eonil, o UTF-32 só será distinguível do UCS4 se tivermos um padrão Unicode que apresenta algo como um UCS5 ou maior.
JasonTrue
@JasonTrue Ainda assim, apenas os resultados são iguais por coincidência, não garantidos pelo design. O mesmo aconteceu no endereçamento de memória de 32 bits, Y2K, UTF16 / UCS2. Ou temos alguma garantia dessa igualdade? Se tivermos, eu ficaria feliz em usar isso. Mas não quero escrever um possível código quebrável . Estou escrevendo um código no nível do caractere, e a falta de uma maneira garantida de transcodificar entre o ponto de código UTF <-> está me incomodando bastante.
Eonil
16

O UTF-8 é definitivamente o caminho a seguir, possivelmente acompanhado pelo UTF-32 para uso interno em algoritmos que precisam de acesso aleatório de alto desempenho (mas que ignora a combinação de caracteres).

Tanto o UTF-16 quanto o UTF-32 (assim como suas variantes LE / BE) sofrem de problemas de resistência, portanto nunca devem ser usados ​​externamente.

Tronic
fonte
9
O acesso aleatório em tempo constante também é possível com o UTF-8, basta usar unidades de código em vez de pontos de código. Talvez você precise de acesso real a pontos de código aleatório, mas nunca vi um caso de uso, e é provável que você queira o acesso aleatório ao cluster de grafema.
15

UTF-16? definitivamente prejudicial. Apenas meu grão de sal aqui, mas existem exatamente três codificações aceitáveis ​​para texto em um programa:

  • ASCII: ao lidar com coisas de baixo nível (por exemplo: microcontroladores) que não podem pagar algo melhor
  • UTF8: armazenamento em mídia de largura fixa, como arquivos
  • pontos de código inteiro ("CP"?): uma matriz dos maiores números inteiros que são convenientes para a sua linguagem e plataforma de programação (decai para ASCII no limite de baixas reservas). Deve ser int32 em computadores mais antigos e int64 em qualquer coisa com endereçamento de 64 bits.

  • Obviamente, as interfaces para o código herdado usam a codificação necessária para que o código antigo funcione corretamente.

David X
fonte
4
@simon buchan, o U+10ffffmax vai sair pela janela quando (não se) ficarem sem codepoints. Dito isso, usar o int32 em um sistema p64 para velocidade é provavelmente seguro, pois duvido que eles excedam U+ffffffffantes que você seja forçado a reescrever seu código para sistemas de 128 bits por volta de 2050. (Esse é o ponto de "usar o maior int que é conveniente" em oposição a 'maior disponível' (que provavelmente seria int256 ou bignums ou algo)).
David X
1
@ David: Unicode 5.2 codifica 107.361 pontos de código. Existem 867.169 pontos de código não utilizados. "quando" é bobo. Um ponto de código Unicode é definido como um número de 0 a 0x10FFFF, uma propriedade da qual o UTF-16 depende. (Também 2050 parece muito para baixo uma estimativa para 128 sistemas de bits quando um sistema de 64-bit pode conter a totalidade da Internet em que é espaço de endereço.)
3
@ David: Seu "quando" estava se referindo à falta de pontos de código Unicode, não um switch de 128 bits que, sim, ocorrerá nos próximos séculos. Diferentemente da memória, não há crescimento exponencial de caracteres, portanto o Unicode Consortium garantiu especificamente que nunca alocará um ponto de código acima U+10FFFF. Essa é realmente uma daquelas situações em que 21 bits são suficientes para qualquer um.
10
@ Simon Buchan: Pelo menos até o primeiro contato. :)
3
O Unicode costumava garantir que também não haveria pontos de código acima do U + FFFF.
Shannon Severance
13

O Unicode define pontos de código de até 0x10FFFF (1.114.112 códigos); todos os aplicativos em execução em ambiente multilíngue que lidam com cadeias / nomes de arquivos etc. devem lidar com isso corretamente.

Utf-16 : abrange apenas 1.112.064 códigos. Embora os que estão no final do Unicode sejam dos planos 15 a 16 (Área de uso particular). Não pode crescer mais no futuro, exceto quebrar o conceito Utf-16 .

Utf-8 : abrange, teoricamente, 2.216.757.376 códigos. O intervalo atual de códigos Unicode pode ser representado por uma sequência máxima de 4 bytes. Não sofre com problema de ordem de bytes , é "compatível" com ascii.

Utf-32 : cobre teoricamente 2 ^ 32 = 4,294,967,296 códigos. Atualmente, ele não é codificado em tamanho variável e provavelmente não será no futuro.

Esses fatos são auto-explicativos. Eu não entendo advogar o uso geral do Utf-16 . É de comprimento variável codificado (não pode ser acessado por índice), tem problemas para cobrir todo o intervalo Unicode , mesmo no momento, a ordem dos bytes deve ser tratada, etc. Não vejo nenhuma vantagem, exceto que é usado nativamente no Windows e em alguns outros lugares. Mesmo que ao escrever código multiplataforma seja provavelmente melhor usar o Utf-8 nativamente e fazer conversões apenas nos pontos finais de maneira dependente da plataforma (como já sugerido). Quando o acesso direto pelo índice é necessário e a memória não é um problema, o Utf-32 deve ser usado.

O principal problema é que muitos programadores que lidam com o Windows Unicode = Utf-16 nem sabem ou ignoram o fato de que ele é codificado em tamanho variável.

O modo como geralmente está na plataforma * nix é muito bom, c strings (char *) interpretadas como codificadas por Utf-8 , c strings amplas (wchar_t *) interpretadas como Utf-32 .

Pavel Machyniak
fonte
7
Nota: O UTF-16 abrange Todos os Unicode, pois o Unicode Consortium decidiu que 10FFFF é o intervalo TOP de Unicode e o comprimento máximo definido de 4 bytes UTF-8 definido e o intervalo explicitamente excluído 0xD800-0xDFFF do intervalo de pontos de código válido e esse intervalo é usado para a criação de pares substitutos. Portanto, qualquer texto Unicode válido pode ser representado com cada uma dessas codificações. Também sobre crescer para o futuro. Parece que 1 milhão de pontos de código não seria suficiente em um futuro distante.
7
@Kerrek: Incorreto: UCS-2 não é uma codificação Unicode válida. Todas as codificações UTF- * por definição podem representar qualquer ponto de código Unicode que seja legal para troca. O UCS-2 pode representar muito menos que isso, além de mais alguns. Repita: UCS-2 não é uma codificação Unicode válida, mais que ASCII.
tchrist
1
"Eu não entendo advogar o uso geral do Utf-8 . É de comprimento variável codificado (não pode ser acessado pelo índice)"
Ian Boyd
9
@Ian Boyd, a necessidade de acessar o caráter individual de uma string em um padrão de acesso aleatório é incrivelmente exagerada. É tão comum quanto querer calcular a diagonal de uma matriz de caracteres, o que é super raro. As seqüências de caracteres são quase sempre processadas seqüencialmente e , como o acesso ao UTF-8 char N + 1, dado que você está no UTF-8, char N é O (1), não há problema. Surpreendentemente, há pouca necessidade de acessar aleatoriamente as strings. Se você acha que vale a pena o espaço de armazenamento para o UTF-32, em vez do UTF-8, é sua opinião, mas para mim, não é um problema.
tchrist
2
@tchrist, eu concederei a você que as seqüências de caracteres são praticamente sempre processadas seqüencialmente se você incluir a iteração reversa como "sequencial" e esticar uma comparação um pouco mais do final de uma string para uma conhecida. Dois cenários muito comuns são truncar espaços em branco a partir do final de strings e verificar a extensão do arquivo no final de um caminho.
Andy Dent
11

Adicione isso à lista:

O cenário apresentado é simples (ainda mais simples, como o apresentarei aqui do que era originalmente!): 1. Um WinForms TextBox fica em um Formulário vazio. Tem um MaxLength definido como 20 .

2.O usuário digita no TextBox ou cola o texto nele.

3.Não importa o que você digite ou cole no TextBox, você está limitado a 20, embora simpaticamente apite em textos além dos 20 (YMMV aqui; alterei meu esquema de som para me dar esse efeito!).

4.O pequeno pacote de texto é enviado para outro lugar, para iniciar uma emocionante aventura.

Agora esse é um cenário fácil, e qualquer pessoa pode escrever isso em seu tempo livre. Eu mesmo escrevi em várias linguagens de programação usando o WinForms, porque estava entediado e nunca tinha tentado antes. E com texto em vários idiomas reais, porque eu sou conectado dessa maneira e tenho mais layouts de teclado do que qualquer um em todo o universo.

Eu até nomeei o formulário Magic Carpet Ride , para ajudar a melhorar o tédio.

Isso não funcionou, pelo que vale a pena.

Em vez disso, inseri os 20 caracteres a seguir no meu formulário Magic Carpet Ride :

0123401234012340123 𠀀

Ah, oh.

Esse último caractere é U + 20000, o primeiro ideograma de extensão B do Unicode (também conhecido como U + d840 U + dc00, para seus amigos íntimos que ele não tem vergonha de se despir, por assim dizer, na frente de ...).

insira a descrição da imagem aqui

E agora temos um jogo de bola.

Porque quando TextBox.MaxLength fala sobre

Obtém ou define o número máximo de caracteres que podem ser inseridos manualmente na caixa de texto.

o que realmente significa é

Obtém ou define o número máximo de unidades de código UTF-16 LE que podem ser inseridas manualmente na caixa de texto e truncará impiedosamente a porcaria viva de qualquer string que tente jogar jogos engraçados com a noção de caractere linguístico de que apenas alguém tão obcecado quanto que o colega da Kaplan vai achar ofensivo (caramba, ele precisa sair mais!).

Vou tentar ver como atualizar o documento ....
Os leitores regulares que se lembrarem da minha série UCS-2 a UTF-16 notarão minha tristeza com a noção simplista de TextBox.MaxLength e como ele deve lidar no mínimo neste caso onde seu comportamento draconiano cria uma sequência ilegal, que outras partes do .Net Framework podem gerar

  • System.Text.EncoderFallbackException: não foi possível converter o caractere Unicode \ uD850 no índice 0 para a página de código especificada. *

exceção se você passar essa string em outro lugar no .Net Framework (como estava fazendo meu colega Dan Thompson).

Agora tudo bem, talvez a série completa UCS-2 a UTF-16 esteja fora do alcance de muitos.
Mas não é razoável esperar que o TextBox.Text não produza um System.Stringque não fará com que outra parte do .Net Framework seja lançada? Quero dizer, não é como se houvesse uma chance na forma de algum evento no controle que informa sobre o próximo truncamento, onde você pode facilmente adicionar a validação mais inteligente - validação que o próprio controle não se importa de fazer. Eu chegaria ao ponto de dizer que esse controle punk está quebrando um contrato de segurança que pode levar a problemas de segurança, se você puder causar exceções inesperadas para encerrar um aplicativo como uma espécie de negação de serviço. Por que qualquer processo, método, algoritmo ou técnica do WinForms produz resultados inválidos?

Fonte: Michael S. Kaplan Blog do MSDN

Matthieu
fonte
Obrigado, link muito bom! Adicionei-o à lista de problemas da pergunta.
9

Eu não diria necessariamente que o UTF-16 é prejudicial. Não é elegante, mas serve para fins de compatibilidade com o UCS-2, assim como o GB18030 com o GB2312 e o UTF-8 com o ASCII.

Mas fazer uma mudança fundamental na estrutura do Unicode no meio do caminho, depois que a Microsoft e a Sun criaram enormes APIs em torno de caracteres de 16 bits, foi prejudicial. O fracasso em divulgar a mudança foi mais prejudicial.

dan04
fonte
8
UTF-8 é um superconjunto de ASCII, mas UTF-16 NÃO é um superconjunto de UCS-2. Embora quase um superconjunto, uma codificação correta do UCS-2 no UTF-8 resulta na abominação conhecida como CESU-8; O UCS-2 não possui substitutos, apenas pontos de código comuns, portanto, eles devem ser traduzidos como tal. A vantagem real do UTF-16 é que é mais fácil atualizar uma base de código UCS-2 do que uma reescrita completa para UTF-8. Engraçado, né?
1
Claro, tecnicamente, o UTF-16 não é um superconjunto do UCS-2, mas quando os U + D800 a U + DFFF foram usados para alguma coisa, exceto os substitutos do UTF-16?
dan04
2
Não importa. Qualquer processamento que não seja passando cegamente pelo bytestream exige que você decodifique os pares substitutos, o que não poderá ser feito se o tratar como UCS-2.
6

O UTF-16 é o melhor compromisso entre manipulação e espaço e é por isso que a maioria das principais plataformas (Win32, Java, .NET) o utilizam para representação interna de strings.

Nemanja Trifunovic
fonte
31
-1 porque o UTF-8 provavelmente é menor ou não é significativamente diferente. Para certos scripts asiáticos, o UTF-8 é de três bytes por glifo, enquanto o UTF-16 é de apenas dois, mas isso é equilibrado pelo fato de o UTF-8 ser apenas um byte para ASCII (que geralmente aparece nos idiomas asiáticos nos nomes de produtos, comandos e outros). coisas). Além disso, nas línguas mencionadas, um glifo transmite mais informações do que um caractere latino, sendo justificado que ele ocupe mais espaço.
32
Eu não consideraria a combinação dos piores lados de ambas as opções um bom compromisso.
18
Não é mais fácil que o UTF-8. Também é de tamanho variável.
27910 luiscubal
36
Deixando de lado os debates sobre os benefícios do UTF-16: O que você citou não é a razão para o Windows, Java ou .NET usar o UTF-16. Windows e Java datam de uma época em que Unicode era uma codificação de 16 bits. UCS-2 era uma escolha razoável naquela época. Quando o Unicode se tornou uma codificação de 21 bits, migrar para o UTF-16 era a melhor escolha que as plataformas existentes tinham. Isso não tinha nada a ver com facilidade de manuseio ou comprometimento de espaço. É apenas uma questão de legado.
Joey #
10
O .NET herda o legado do Windows aqui.
Joey #
6

Eu nunca entendi o objetivo do UTF-16. Se você deseja uma representação com maior eficiência de espaço, use UTF-8. Se você deseja tratar o texto como tamanho fixo, use UTF-32. Se você não quiser, use UTF-16. Pior ainda, como todos os caracteres comuns (plano multilíngue básico) do UTF-16 se encaixam em um único ponto de código, os bugs que assumem que o UTF-16 tem tamanho fixo serão sutis e difíceis de encontrar, enquanto que se você tentar fazer isso isso com UTF-8, seu código falhará rápido e alto assim que você tentar internacionalizar.

dsimcha
fonte
6

Como ainda não posso comentar, posto isso como resposta, pois parece que não posso entrar em contato com os autores de utf8everywhere.org. É uma pena que eu não receba automaticamente o privilégio de comentar, pois tenho reputação suficiente em outras alterações de pilha.

Isso significa um comentário para a Opinião: Sim, o UTF-16 deve ser considerado uma resposta prejudicial .

Uma pequena correção:

Para impedir que alguém passe acidentalmente um UTF-8 char*para versões de string ANSI das funções da API do Windows, deve-se definir UNICODE, não _UNICODE. _UNICODEmapeia funções como _tcslenpara wcslen, e não MessageBoxpara MessageBoxW. Em vez disso, o UNICODEdefine cuida do último. Para prova, isso é do WinUser.hcabeçalho do MS Visual Studio 2005 :

#ifdef UNICODE
#define MessageBox  MessageBoxW
#else
#define MessageBox  MessageBoxA
#endif // !UNICODE

No mínimo, esse erro deve ser corrigido utf8everywhere.org.

Uma sugestão:

Talvez o guia deva conter um exemplo de uso explícito da versão de cadeia ampla de uma estrutura de dados, para tornar menos fácil perder / esquecer. O uso de versões de cadeia ampla de estruturas de dados, além de usar versões de funções de cadeia ampla, torna ainda menos provável que alguém acidentalmente chame uma versão de cadeia de caracteres ANSI dessa função.

Exemplo do exemplo:

WIN32_FIND_DATAW data; // Note the W at the end.
HANDLE hSearch = FindFirstFileW(widen("*.txt").c_str(), &data);
if (hSearch != INVALID_HANDLE_VALUE)
{
    FindClose(hSearch);
    MessageBoxW(nullptr, data.cFileName, nullptr, MB_OK);
}
Jelle Geerts
fonte
Acordado; obrigado! Vamos atualizar o documento. O documento ainda precisa de mais desenvolvimento e adição de informações sobre bancos de dados. Estamos felizes em receber contribuições de formulações.
Pavel Radzivilovsky
@PavelRadzivilovsky _UNICODEainda está lá :(
cubuspl42
obrigado por lembrar. cubus, Jelle, você gostaria de um usuário para o nosso SVN?
Pavel Radzivilovsky
@ Pavel Claro, agradeceria!
precisa saber é o seguinte
@JelleGeerts: Peço desculpas por esse atraso. Você sempre pode entrar em contato conosco por nossos e-mails (vinculados no manifesto) ou pelo Facebook. Somos fáceis de encontrar. Embora eu acredite que resolvemos o problema que você trouxe aqui (e eu o creditei lá), todo o debate UTF-8 vs UTF-16 ainda é relevante. Se você tem mais a contribuir, não hesite em entrar em contato conosco através desses canais privados.
ybungalobill
5

Alguém disse que UCS4 e UTF-32 eram os mesmos. Não, mas eu sei o que você quer dizer. Um deles é uma codificação do outro, no entanto. Eu gostaria que eles pensassem em especificar endianness desde o primeiro, para que não tivéssemos a batalha de endianess aqui também. Eles não poderiam ter visto isso chegando? Pelo menos UTF-8 é o mesmo em todos os lugares (a menos que alguém esteja seguindo a especificação original com 6 bytes).

Se você usa UTF-16 , deve incluir a manipulação de caracteres multibyte. Você não pode ir para o enésimo caractere indexando 2N em uma matriz de bytes. Você precisa caminhar ou ter índices de caracteres. Caso contrário, você escreveu um bug.

O rascunho atual do C ++ diz que o UTF-32 e o UTF-16 podem ter variantes little-endian, big-endian e não especificadas. Realmente? Se o Unicode tivesse especificado que todo mundo tinha que fazer little-endian desde o começo, tudo seria mais simples. (Eu também ficaria bem com o big endian.) Em vez disso, algumas pessoas o implementaram de uma maneira, outras da outra, e agora estamos presos à tolice por nada. Às vezes, é embaraçoso ser um engenheiro de software.

user22815
fonte
Endianess não especificado deve incluir a BOM como o primeiro caractere, usado para determinar de que maneira a string deve ser lida. Atualmente, UCS-4 e UTF-32 são os mesmos atualmente, ou seja, um valor UCS numérico entre 0 e 0x10FFFF armazenado em um número inteiro de 32 bits.
5
@Tronic: Tecnicamente, isso não é verdade. Embora o UCS-4 possa armazenar qualquer número inteiro de 32 bits, o UTF-32 é proibido de armazenar pontos de código sem caracteres ilegais para intercâmbio, como 0xFFFF, 0xFFFE e todos os substitutos. UTF é uma codificação de transporte, não interna.
tchrist 11/08/11
Problemas de endianidade são inevitáveis, desde que diferentes processadores continuem usando ordens de bytes diferentes. No entanto, pode ter sido bom se houvesse uma ordem de bytes "preferida" para armazenamento de arquivos do UTF-16.
Qwertie
Mesmo que o UTF-32 tenha largura fixa para pontos de código , não é largura fixa para caracteres . (Ouviu falar de algo chamado "combinar caracteres"?) Portanto, você não pode acessar o caractere N'th simplesmente indexando 4N na matriz de bytes.
Musiphil
2

Não acho que seja prejudicial se o desenvolvedor for cuidadoso o suficiente.
E eles devem aceitar essa troca se souberem bem também.

Como desenvolvedor de software japonês, acho que o UCS-2 é grande o suficiente e a limitação do espaço aparentemente simplifica a lógica e reduz a memória de tempo de execução; portanto, usar utf-16 sob a limitação do UCS-2 é bom o suficiente.

Há um sistema de arquivos ou outro aplicativo que assume que os pontos de código e bytes sejam proporcionais, para garantir que o número bruto do ponto de código seja adequado para algum armazenamento de tamanho fixo.

Um exemplo é NTFS e VFAT especificando UCS-2 como sua codificação de armazenamento de nome de arquivo.

Se esse exemplo realmente quiser se estender para oferecer suporte ao UCS-4, eu poderia concordar em usar o utf-8 para tudo, mas o comprimento fixo tem bons pontos, como:

  1. pode garantir o tamanho pelo comprimento (tamanho dos dados e comprimento do ponto de código é proporcional)
  2. pode usar o número de codificação para pesquisa de hash
  3. dados não compactados são de tamanho razoável (em comparação com utf-32 / UCS-4)

No futuro, quando a capacidade de memória / processamento for barata, mesmo em qualquer dispositivo incorporado, podemos aceitar que o dispositivo seja um pouco lento por falhas extras de cache ou falhas de página e uso de memória adicional, mas isso não acontecerá no futuro próximo, eu acho ...

holmes
fonte
3
Para quem lê este comentário, vale a pena notar que UCS-2 não é a mesma coisa que UTF-16. Por favor, procure as diferenças para entender.
Mikebabcock 19/12/12
1

"Uma das codificações mais populares, a UTF-16, deve ser considerada prejudicial?"

Possivelmente, mas as alternativas não devem necessariamente ser vistas como muito melhores.

A questão fundamental é que existem muitos conceitos diferentes sobre: ​​glifos, caracteres, pontos de código e seqüências de bytes. O mapeamento entre cada um deles não é trivial, mesmo com o auxílio de uma biblioteca de normalização. (Por exemplo, alguns caracteres em idiomas europeus que são escritos com um script baseado em latim não são escritos com um único ponto de código Unicode. E isso é no final mais simples da complexidade!) O que isso significa é que, para corrigir tudo, é surpreendentemente surpreendente. difícil; erros bizarros são esperados (e, em vez de apenas reclamar aqui, informe aos mantenedores do software em questão).

A única maneira pela qual o UTF-16 pode ser considerado prejudicial, por oposição ao UTF-8, é que ele possui uma maneira diferente de codificar pontos de código fora do BMP (como um par de substitutos). Se o código deseja acessar ou iterar por ponto de código, isso significa que ele precisa estar ciente da diferença. OTOH, isso significa que um corpo substancial de código existente que assume "caracteres" sempre pode ser ajustado em uma quantidade de dois bytes - uma suposição bastante comum, se incorreta - pode pelo menos continuar trabalhando sem reconstruir tudo. Em outras palavras, pelo menos você consegue ver os personagens que não estão sendo tratados corretamente!

Eu viraria sua pergunta de cabeça para baixo e diria que toda a maldição do Unicode deveria ser considerada prejudicial e todos deveriam usar uma codificação de 8 bits, exceto que eu vi (nos últimos 20 anos) onde isso leva: horrível confusão sobre as várias codificações ISO 8859, além de todo o conjunto de codificações usadas para cirílico e o conjunto EBCDIC e ... bem, o Unicode por todas as suas falhas supera isso. Se ao menos não fosse um compromisso tão desagradável entre os mal-entendidos de diferentes países.

Donal Fellows
fonte
Sabendo a nossa sorte, daqui a alguns anos ficaremos sem espaço no UTF-16. Meh.
Donal Fellows
3
A questão fundamental é que o texto é enganosamente difícil. Nenhuma abordagem para representar essas informações de maneira digital pode ser simples. É a mesma razão que as datas são difíceis, os calendários são difíceis, o tempo é difícil, os nomes pessoais são difíceis, os endereços postais são difíceis: sempre que as máquinas digitais se cruzam com construções culturais humanas, a complexidade entra em erupção. É um fato da vida. Os seres humanos não funcionam na lógica digital.
Aristóteles Pagaltzis