Dlaczego niektóre znaki Unicode nie będą drukowane na moim terminalu?


16

Używam Arch Linux z prostym terminalem, używając czcionki Adobe Source Code Pro. Moje ustawienia regionalne są poprawnie ustawione na LANG=en_US.UTF-8.

Chcę wydrukować na moim terminalu znaki Unicode reprezentujące karty do gry. Korzystam z Wikipedii w celach informacyjnych .

Znaki Unicode dla kolorów kart działają dobrze. Na przykład wydawanie

$ printf "\u2660"

drukuje czarne serce na ekranie.

Mam jednak problemy z określonymi kartami do gry. Wydawanie

$ printf "\u1F0A1"

wypisuje symbol Ἂ1zamiast asa pik 🂡. Co jest nie tak?

Ten problem występuje na kilku terminalach (urxvt, xterm, termite) i na każdej wypróbowanej czcionce (DejaVu, Inconsolata).


Ostrzeżenie: jeśli jest to obsługiwane przez printf, jest to niestandardowe rozszerzenie. Więc nie oczekuj, że takie ucieczki w ogóle zadziałają. Zobacz: pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html
schily

Odpowiedzi:


27

help printfodkłada na printf(1)interpretowane sekwencje specjalne, a dokumentacja GNU printf mówi:

printfinterpretuje składnie dwóch znaków wprowadzone w ISO C 99: \udla 16-bitowych znaków Unicode (ISO / IEC 10646), określonych jako cztery cyfry szesnastkowe hhhh , i \Udla 32-bitowych znaków Unicode, określonych jako osiem cyfr szesnastkowych hhhhhhhh . printfwyświetla znaki Unicode zgodnie z LC_CTYPEustawieniami regionalnymi. Ta składnia nie może określać znaków Unicode z zakresu U + 0000… U + 009F, U + D800… U + DFFF, z wyjątkiem U + 0024 ($), U + 0040 (@) i U + 0060 (`) .

Coś podobnego podano w podręczniku Bash dla ANSI C Quoting i echo:

\uHHHH
znak Unicode (ISO / IEC 10646), którego wartość jest wartością szesnastkową HHHH (od jednej do czterech cyfr szesnastkowych)

\UHHHHHHHH
znak Unicode (ISO / IEC 10646), którego wartość jest wartością szesnastkową HHHHHHHH (od jednej do ośmiu cyfr szesnastkowych)

W skrócie: \unie jest dla 5 cyfr szesnastkowych. To jest \U:

# printf "\u2660 \u1F0A1 \U1F0A1\n"
 1 🂡

2

Odpowiedź Muru jest całkowicie poprawna, ale tylko w celu wyjaśnienia jednego punktu:

Podczas drukowania \u1F0A1jest to interpretowane jako szesnastobitowe wyjście Unicode \u1F0A, po którym następuje dosłowny znak 1(ponieważ \uzajmuje cztery następujące znaki, nie więcej, nie mniej). U + 1F0A daje następnie grecką alfę z kilkoma znakami diakrytycznymi ( dokładniej grecka litera Alpha z Psili i Varią ).

Jeśli chcesz więcej niż szesnaście bitów w ucieczce Unicode, musisz użyć \U, który wymaga heksa o wartości ośmiu znaków: \U0001F0A1da ci kartę do gry.


\U0001F0A1jest w rzeczywistości bardziej przenośny niż \U1F0A1. Jest to samodzielne printfnarzędzie GNU , które jako pierwsze wprowadziło te \uXXXX/ \UXXXXXXXXsekwencje i wymaga 4 cyfr dla \ui 8 dla \U. Inne printfimplementacje, takie jak wbudowana powłoka GNU, ksh93 i zsh, są bardziej luźne. W każdym razie printf '\u/\U'nie jest POSIX. POSIX określi jednak parametry zsh $'\U1F0A1'i nie będzie wymagał wszystkich 8 cyfr.
Stéphane Chazelas,

@ StéphaneChazelas Co ciekawe, zawsze myślałem, że POSIX będzie pasował do ośmiocyfrowej. Zakładam, że ośmiocyfrowa wersja jest nadal ważna w Zsh, jeśli chcesz uniknąć przechwytywania dodatkowych liter i cyfr po kodzie?
Draconis,

Tak, \uxxxxjest do 4 cyfr i \Uxxxxxxxxjest do 8 cyfr. Zauważ, że Unicode jest teraz ograniczony do współrzędnych kodowych od 0 do 0x10FFFF (ograniczenie wprowadzone przez UTF16), więc punkty kodowe nigdy nie będą miały więcej niż 6 cyfr (nadal \U123456789będą interpretowane jako znak punktu kodowego 0x12345678, po którym 9nastąpi błąd). Specyfikacja POSIX dla $'\u\U'wciąż nie jest sfinalizowana (patrz austingroupbugs.net/view.php?id=249 ). We wcześniejszej wersji wymagały wszystkich 4/8 cyfr, ale zmieniły się później (na moją prośbę).
Stéphane Chazelas,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.