Por que alguns caracteres unicode não são impressos no meu terminal?

16

Estou executando o Arch Linux com um terminal simples usando a fonte do Adobe Source Code Pro. Minha localidade está definida corretamente como LANG=en_US.UTF-8.

Quero imprimir caracteres Unicode representando cartas de baralho no meu terminal. Estou usando a Wikipedia para referência .

Os caracteres Unicode para cartões funcionam bem. Por exemplo, emitindo

$ printf "\u2660"

imprime um coração preto na tela.

No entanto, estou tendo problemas com cartas de baralho específicas. Emissão

$ printf "\u1F0A1"

imprime o símbolo em Ἂ1vez do ás de espadas 🂡. O que está acontecendo de errado?

Esse problema persiste em vários terminais (urxvt, xterm, cupim) e em todas as fontes que eu tentei (DejaVu, Inconsolata).

Brian Fitzpatrick
fonte
Aviso: se isso for tratado pelo printf, é um aprimoramento não padrão. Portanto, não espere que essas fugas funcionem. Veja: pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
schily

Respostas:

27

help printfadia printf(1)para as seqüências de escape interpretadas, e os documentos para GNU printf dizem:

printfinterpreta as sintaxes de dois caracteres introduzidos na ISO C 99: \upara caracteres Unicode de 16 bits (ISO / IEC 10646), especificados como quatro dígitos hexadecimais hhhh , e \Upara caracteres Unicode de 32 bits, especificados como oito dígitos hexadecimais hhhhhhhh . printfgera os caracteres Unicode de acordo com o LC_CTYPEcódigo do idioma. Caracteres Unicode nos intervalos U + 0000… U + 009F, U + D800… U + DFFF não podem ser especificados por esta sintaxe, exceto U + 0024 ($), U + 0040 (@) e U + 0060 (`) .

Algo semelhante é especificado no manual do Bash para cotação ANSI C e echo:

\uHHHH
o caractere Unicode (ISO / IEC 10646) cujo valor é o valor hexadecimal HHHH (um a quatro dígitos hexadecimais)

\UHHHHHHHH
o caractere Unicode (ISO / IEC 10646) cujo valor é o valor hexadecimal HHHHHHHH (um a oito dígitos hexadecimais)

Em resumo: \unão é para 5 dígitos hexadecimais. É \U:

# printf "\u2660 \u1F0A1 \U1F0A1\n"
 1 🂡
muru
fonte
2

A resposta de Muru está completamente correta, mas apenas para esclarecer um ponto:

Quando você está imprimindo \u1F0A1, isso é interpretado como uma fuga Unicode de dezesseis bits \u1F0A, seguida pelo caractere literal 1(já que \uocupa os quatro caracteres a seguir , nem mais nem menos). U + 1F0A então fornece um alfa grego com dois diacríticos ( letra maiúscula grega Alpha com Psili e Varia , para ser mais preciso).

Se você quiser mais de dezesseis bits na sua fuga Unicode, precisará usar \U, o que requer oito caracteres hexadecimais: \U0001F0A1fornecerá o cartão de jogo.

Draconis
fonte
\U0001F0A1é realmente mais portátil que \U1F0A1. É o printfutilitário independente GNU que introduziu essas \uXXXX/ \UXXXXXXXXseqüências e requer 4 dígitos \ue 8 para \U. Outras printfimplementações como o embutido no shell GNU, ksh93 e zsh são mais relaxadas. Em qualquer caso, printf '\u/\U'não é POSIX. No entanto, o POSIX especificará zsh $'\U1F0A1'e não exigirá todos os 8 dígitos.
Stéphane Chazelas 13/08/19
@ StéphaneChazelas Interessante, eu sempre achei que o POSIX iria com o de oito dígitos. Presumo que a versão de oito dígitos ainda seja válida no zsh, se você quiser evitar capturar letras e números extras após o código?
Draconis
Sim, \uxxxxé até 4 dígitos e \Uxxxxxxxxé até 8 dígitos. Observe que agora o Unicode está limitado aos pontos de código de 0 a 0x10FFFF (uma limitação trazida por UTF16), portanto, os pontos de código nunca terão mais de 6 dígitos (ainda \U123456789seriam interpretados como o caractere do ponto de código 0x12345678 seguido 9e falha). A especificação POSIX para $'\u\U'ainda não está finalizada (consulte austingroupbugs.net/view.php?id=249 ). Em um rascunho anterior, eles exigiam todos os 4/8 dígitos, mas que foram alterados mais tarde (mediante solicitação).
Stéphane Chazelas 13/08/19