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


16

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

Наприклад, U + 1F615 є офіційним кодом консорціуму Unicode для "плутаного обличчя" 😕

Оскільки мене часто плутають, у мене є сильна спорідненість до цього символу.

Представлення U + 1F615 мене бентежить, тому що я вважав, що для єдиного кодування для символів Unicode потрібні 8, 16, 24 або 32 біта, тоді як для 5-ти шістнадцяткових цифр потрібно 5x4 = 20 біт.

Я виявив, що цей символ, здається, представлений зовсім іншою шістнадцятковою рядком у баші:

$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-бітний код?

Чи існує зв’язок між цими 2 числами?

Відповіді:


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 році автономною printfутилітою GNU (на відміну від 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слідує інша дійсна шістнадцяткова цифра, то це вважатиметься частиною послідовності відходу. Щоб змусити його працювати незалежно від того, за чим він слідує, йому повинно бути достатньо \U0001F615
нульових

@kasperd, спасибі Так, це варто відзначити. Я це включив у відповідь.
Стефан Шазелас

7

Ось спосіб перетворення з UTF-32 (великий ендіан) в 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.