Как преобразовать смайлик, указанный кодом U + xxxxx, в utf-8?


16

Смайлики, по-видимому, указываются с использованием формата U + xxxxx,
где каждый x является шестнадцатеричной цифрой.

Например, U + 1F615 является официальным кодом Консорциума Unicode для «запутанного лица» 😕

Как я часто путаюсь, я очень привязан к этому символу.

Представление U + 1F615 сбивает меня с толку, потому что я думал, что единственные возможные кодировки для символов Юникода требуют 8, 16, 24 или 32 бита, тогда как 5 шестнадцатеричных цифр требуют 5x4 = 20 бит.

Я обнаружил, что этот символ представляется совершенно другой шестнадцатеричной строкой в ​​bash:

$echo -n 😕 | hexdump
0000000 f0 9f 98 95                                    
0000004

$echo -e "\xf0\x9f\x98\x95"
😕

$PS1=$'\xf0\x9f\x98\x95  >'
😕  >

Я ожидал, что U + 1F615 преобразуется во что-то вроде \ x00 \ x01 \ xF6 \ x15 .

Я не вижу связи между этими двумя кодировками?

Когда я ищу символ в официальном списке Консорциума Unicode , я хотел бы иметь возможность использовать этот код напрямую, без необходимости вручную его утомительно конвертировать. т.е.

  • найти символ на какой-то веб-странице
  • скопировать его в буфер обмена веб-браузера
  • вставив его в bash, чтобы повторить через hexdump, чтобы обнаружить РЕАЛЬНЫЙ код.

Могу ли я использовать этот 20-битный код, чтобы определить, что такое 32-битный код?

Существует ли связь между этими двумя числами?

Ответы:


20

UTF-8кодировка переменной длины Unicode Это разработано, чтобы быть надмножеством ASCII. Посмотрите Википедию для деталей о кодировке. \x00 \x01 \xF6 \x15будет UCS-4BEили UTF-32BEкодировка.

Чтобы перейти от кодовой точки Unicode к кодировке UTF-8, предполагая, что charmap локали - UTF-8 (см. Вывод locale charmap), это просто:

$ printf '\U1F615\n'
😕
$ echo -e '\U1F615'
😕
$ confused_face=$'\U1F615'

Последний будет в следующей версии стандарта POSIX .

AFAIK, что синтаксис был введен в 2000 году автономной GNU printfутилиты (в отличие от printfполезности GNU оболочки), привел к echo/ printf/ $'...'встроенных команд первой по zsh2003 , ksh93 в 2004 году, Баш в 2010 году (хотя не работает должным образом там до 2014 года ), но был явно вдохновлен другими языками.

ksh93также поддерживает это как printf '\x1f615\n'и printf '\u{1f615}\n'.

$'\uXXXX'и $'\UXXXXXXXX'поддерживаются zsh, bash, ksh93, mkshи FreeBSD sh, GNU printf, GNU echo.

Некоторые требуют всех цифр (в \U0001F615отличие от \U1F615), хотя это может измениться в будущих версиях, так как POSIX позволит меньше цифр. В любом случае, вам нужны все цифры, если \UXXXXXXXXпосле них следуют шестнадцатеричные цифры, как в случае \U0001F615FOX, как \U1F615FOXэто было бы $'\U001F615F'OX.

Некоторые расширяются до символов в кодировке текущей локали во время синтаксического анализа строки или во время ее расширения, некоторые только в UTF-8 независимо от локали. Если символ недоступен в кодировке текущей локали, поведение будет различным в разных оболочках.

Поэтому для лучшей переносимости лучше всего использовать его только в локалях UTF-8, использовать все цифры и использовать его в $'...':

printf '%s\n' $'\U0001F615'

Обратите внимание, что:

LC_ALL=C.UTF-8; printf '%s\n' $'\U0001F615'

или:

{
  LC_ALL=C.UTF-8
  printf '%s\n' $'\U0001F615'
}

Не будет работать со всеми оболочками ( в том числе bash) , так как $'\U0001F615'это анализируется , прежде чем LC_ALLназначен. (также обратите внимание, что нет гарантии, что система будет иметь локаль с именем C.UTF-8)

Вам нужно:

LC_ALL=C.UTF-8; eval "confused_face=$'\U0001F615'"

Или:

LC_ALL=C.UTF-8
printf '%s\n' $'\U0001F615'

(не входит в составную команду или функцию).


Для обратного, чтобы перейти от кодировки UTF-8 к кодовой точке Unicode, посмотрите этот другой вопрос или тот .

$ unicode 😕 
U+1F615 CONFUSED FACE
UTF-8: f0 9f 98 95  UTF-16BE: d83dde15  Decimal: 😕
😕
Category: So (Symbol, Other)
Bidi: ON (Other Neutrals)

$ perl -CA -le 'printf "%x\n", ord shift' 😕
1f615

2
Обратите внимание, что если \U1F615за ним следует другая действительная шестнадцатеричная цифра, она будет считаться частью escape-последовательности. Чтобы заставить его работать независимо от того, что за ним следует, должно быть достаточно \U0001F615
начальных

@kasperd, спасибо. Да, это стоит отметить. Я включил это в ответ.
Стефан Шазелас

7

Вот способ конвертировать из UTF-32 (big endian) в UTF-8

$ confused=$(echo -ne "\x0\x01\xF6\x15" | iconv -f UTF-32BE -t UTF-8)     
$ echo $confused 
😕

Вы заметите здесь свое шестнадцатеричное значение 0x01F615, дополненное дополнительным 0, чтобы заполнить 32 бита.

Страница Википедии на UTF-8 объясняет преобразование из Unicode в элемент кода его представления UTF-8 очень четко. Но попытка сделать это самостоятельно в сценариях оболочки может быть не лучшей идеей.

UTF-32 имеет фиксированную ширину, и соответствие между кодовой точкой и представлением UTF-32 тривиально - значение одинаково.


6

Хороший способ сделать это в своей голове или на бумаге:

  1. Выясните, сколько это будет байтов: значения в U + 0080 составляют один байт, иначе в U + 0800 - 2 байта, еще в U + 10000 - 3 байта, еще 4 байта. В вашем случае 4 байта.

  2. Преобразование шестнадцатеричного в восьмеричный: 0373025.

  3. Начиная с конца, очистить от 2 восьмеричных цифр в один раз , чтобы получить последовательность восьмеричных значений: 037 030 025.

  4. Если у вас меньше восьмеричные значения , чем ожидаемое число байтов, добавить дополнительный 0 в начале: 000 037 030 025.

  5. Для всех , кроме первого, прибавить 0200туда: 000 0237 0230 0225.

  6. Для первых, добавьте , 0300если ожидаемая длина 2, 0340если это 3 или , 0360если это 4, чтобы получить: 360 0237 0230 0225.

Теперь записайте в виде строки из восьмеричных побегов: \360\237\230\225. При желании конвертировать обратно в гекс, если хотите.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.