16

I'm running Arch Linux with simple terminal using the Adobe Source Code Pro font. My locale is correctly set to LANG=en_US.UTF-8.

I want to print Unicode characters representing playing cards to my terminal. I'm using Wikipedia for reference.

The Unicode characters for card suits work fine. For example, issuing

$ printf "\u2660"

prints a black heart to the screen.

However, I'm having trouble with specific playing cards. Issuing

$ printf "\u1F0A1"

prints the symbol Ἂ1 instead of the ace of spades 🂡. What's going wrong?

This problem persists across several terminals (urxvt, xterm, termite) and every font I've tried (DejaVu, Inconsolata).

27

help printf defers to printf(1) for the escape sequences interpreted, and the docs for GNU printf says:

printf interprets two character syntaxes introduced in ISO C 99: \u for 16-bit Unicode (ISO/IEC 10646) characters, specified as four hexadecimal digits hhhh, and \U for 32-bit Unicode characters, specified as eight hexadecimal digits hhhhhhhh. printf outputs the Unicode characters according to the LC_CTYPE locale. Unicode characters in the ranges U+0000…U+009F, U+D800…U+DFFF cannot be specified by this syntax, except for U+0024 ($), U+0040 (@), and U+0060 (`).

Something similar is specified in the Bash manual for ANSI C Quoting and echo:

\uHHHH
the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHH (one to four hex digits)

\UHHHHHHHH
the Unicode (ISO/IEC 10646) character whose value is the hexadecimal value HHHHHHHH (one to eight hex digits)

In short: \u is not for 5 hex digits. It's \U:

# printf "\u2660 \u1F0A1 \U1F0A1\n"
♠ Ἂ1 🂡
2

Muru's answer is completely correct, but just to clarify one point:

When you're printing \u1F0A1, that's interpreted as a sixteen-bit Unicode escape \u1F0A, followed by the literal character 1 (since \u takes the following four characters, no more, no less). U+1F0A then gives , a Greek alpha with a couple diacritics on it (Greek Capital Letter Alpha with Psili and Varia, to be precise).

If you want more than sixteen bits in your Unicode escape, you need to use \U, which takes eight characters' worth of hex: \U0001F0A1 will give you the playing card.

  • \U0001F0A1 is actually more portable than \U1F0A1. It's the GNU standalone printf utility which first introduced those \uXXXX/\UXXXXXXXX sequences and it does require 4 digits for \u and 8 for \U. Other printf implementations like the builtin of the GNU shell, ksh93 and zsh are more lax. In any case printf '\u/\U' is not POSIX. POSIX is however going to specify zsh's $'\U1F0A1' and will not require all 8 digits. – Stéphane Chazelas Aug 13 at 19:47
  • @StéphaneChazelas Interesting, I'd always figured that POSIX would go with the eight-digit one. I assume the eight-digit version is still valid in zsh if you want to avoid capturing extra letters and numbers after the code? – Draconis Aug 13 at 19:54
  • Yes, \uxxxx is up to 4 digits and \Uxxxxxxxx is up to 8 digits. Note that Unicode is now limited to codepoints 0 to 0x10FFFF (a limitation brought by UTF16) so code points will never have more than 6 digits (still \U123456789 would be interpreted as the character of code point 0x12345678 followed by 9 and fail). The POSIX specification for $'\u\U' is still not finalised (see austingroupbugs.net/view.php?id=249). In an earlier draft, they required all 4/8 digits but that changed later (upon my request). – Stéphane Chazelas Aug 13 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.