Jak przekonwertować emotikon określony kodem U + xxxxx na utf-8?

16

Emotikony wydają się być określone przy użyciu formatu U + xxxxx, w
którym każdy x jest cyfrą szesnastkową.

Na przykład U + 1F615 to oficjalny kod konsorcjum Unicode dla „zdezorientowanej twarzy” 😕

Ponieważ często jestem zdezorientowany, mam silne powinowactwo do tego symbolu.

Reprezentacja U + 1F615 jest dla mnie myląca, ponieważ myślałem, że jedyne możliwe kodowanie znaków Unicode wymaga 8, 16, 24 lub 32 bitów, podczas gdy 5 cyfr szesnastkowych wymaga 5x4 = 20 bitów.

Odkryłem, że ten symbol wydaje się być reprezentowany przez zupełnie inny ciąg szesnastkowy w bash:

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

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

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

Spodziewałbym się, że U + 1F615 przekonwertuje na coś takiego jak \ x00 \ x01 \ xF6 \ x15 .

Nie widzę związku między tymi 2 kodowaniami?

Kiedy szukam symbolu na oficjalnej liście konsorcjum Unicode , chciałbym móc użyć tego kodu bezpośrednio, bez konieczności ręcznej konwersji w tak żmudny sposób. to znaczy

  • znalezienie symbolu na jakiejś stronie internetowej
  • kopiowanie do schowka przeglądarki internetowej
  • wklejenie go w bash, aby wywołać echo przez zrzut heksowy i odkryć PRAWDZIWY kod.

Czy mogę użyć tego 20-bitowego kodu, aby ustalić, co to jest 32-bitowy kod?

Czy istnieje związek między tymi 2 liczbami?

Alex Ryan
źródło

Odpowiedzi:

20

UTF-8jest kodowaniem Unicode o zmiennej długości . Został zaprojektowany jako nadzbiór ASCII. Szczegółowe informacje na temat kodowania znajdują się w Wikipedii . \x00 \x01 \xF6 \x15byłoby UCS-4BElub UTF-32BEkodowanie.

Aby przejść z punktu kodu Unicode do kodowania UTF-8, zakładając, że karta lokalizacji jest UTF-8 (zobacz wynik locale charmap), to po prostu:

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

Ten ostatni będzie w następnej wersji standardu POSIX .

AFAIK, że składnia została wprowadzona w 2000 roku przez autonomiczny GNU printfnarzędzia (w przeciwieństwie do printfnarzędzia GNU skorupkach), sprowadzony do echo/ printf/ $'...'builtins pierwszy od zsh2003 , ksh93 w 2004 roku, bash w 2010 roku (choć nie działa prawidłowo tam do 2014 roku ), ale oczywiście był inspirowany innymi językami.

ksh93obsługuje również jako printf '\x1f615\n'i printf '\u{1f615}\n'.

$'\uXXXX'i $'\UXXXXXXXX'są obsługiwane przez zsh, bash, ksh93, mkshi FreeBSD sh, GNU printf, GNU echo.

Niektóre wymagają wszystkich cyfr (w \U0001F615przeciwieństwie do \U1F615), ale prawdopodobnie zmieni się to w przyszłych wersjach, ponieważ POSIX pozwoli na mniej cyfr. W każdym razie potrzebujesz wszystkich cyfr, jeśli po nich \UXXXXXXXXmają następować cyfry szesnastkowe jak w \U0001F615FOX, jak \U1F615FOXby to było $'\U001F615F'OX.

Niektóre rozwijają się do znaków w bieżącym kodowaniu ustawień narodowych w czasie analizowania łańcucha lub w czasie jego rozwijania, inne tylko w UTF-8, niezależnie od ustawień regionalnych. Jeśli znak nie jest dostępny w kodowaniu bieżących ustawień regionalnych, zachowanie różni się w zależności od powłoki.

Tak więc, dla najlepszej przenośności, najlepiej jest używać go tylko w ustawieniach regionalnych UTF-8 i używać wszystkich cyfr, i używać go w $'...':

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

Uwaga:

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

lub:

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

Nie będzie działać ze wszystkimi powłokami (w tym bash), ponieważ $'\U0001F615'jest analizowany przed LC_ALLprzypisaniem. (należy również pamiętać, że nie ma gwarancji, że system będzie miał nazwę narodową o nazwie C.UTF-8)

Potrzebujesz:

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

Lub:

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

(nie w poleceniu lub funkcji złożonej).


Odwrotnie, aby przejść z kodowania UTF-8 do punktu kodowego Unicode, zobacz to inne pytanie lub to .

$ 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
Stéphane Chazelas
źródło
2
Zauważ, że jeśli \U1F615po nim następuje kolejna ważna cyfra szesnastkowa, to będzie to założone, że jest częścią sekwencji specjalnej. Aby działało niezależnie od tego, po czym następuje, musi mieć wystarczającą liczbę zer wiodących, aby mieć dokładnie osiem cyfr:\U0001F615
kasperd
@kasperd, dzięki. Tak, warto to zauważyć. Zawarłem to w odpowiedzi.
Stéphane Chazelas,
7

Oto sposób konwersji z UTF-32 (big endian) na UTF-8

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

Zauważysz tam swoją wartość szesnastkową 0x01F615, wypełnioną dodatkowym początkowym 0, aby wypełnić 32 bity.

Strona Wikipedii na temat UTF-8 bardzo wyraźnie wyjaśnia transformację z punktu kodowego Unicode na jego reprezentację UTF-8. Ale próba zrobienia tego samemu w skryptach powłoki może nie być najlepszym pomysłem.

UTF-32 ma stałą szerokość, a zgodność między punktem kodowym a reprezentacją UTF-32 jest banalna - wartość jest taka sama.

Mata
źródło
6

Miły sposób na zrobienie tego w głowie lub na papierze:

  1. Zastanów się, ile to będzie bajtów: wartości pod U + 0080 to jeden bajt, w przeciwnym razie pod U + 0800 są 2 bajty, w innym przypadku pod U + 10000 są 3 bajty, w przeciwnym razie 4 bajty. W twoim przypadku 4 bajty.

  2. Konwertuj szesnastkowy na ósemkowy: 0373025 .

  3. Począwszy od końca, peel off 2 cyfry ósemkowe na raz, aby uzyskać sekwencję wartości ósemkowe: 037 030 025.

  4. Jeśli masz mniej niż wartości ósemkowe oczekiwanej liczby bajtów, dodać dodatkową 0 na początku: 000 037 030 025.

  5. Dla wszystkich, ale po pierwsze, na dodatek 0200dostać: 000 0237 0230 0225.

  6. Na pierwszy, dodaj 0300jeśli spodziewana długość wynosi 2, 0340jeśli jest to 3, lub 0360jeśli jest to 4, otrzymujemy: 360 0237 0230 0225.

Teraz napisać jako ciąg ósemkowe ucieczek: \360\237\230\225. Opcjonalnie możesz przekonwertować z powrotem na hex, jeśli chcesz.

R .. GitHub ZATRZYMAJ LÓD
źródło