Come convertire un'emoticon specificata da un codice U + xxxxx in utf-8?


16

Le emoticon sembrano essere specificate usando un formato di U + xxxxx in
cui ogni x è una cifra esadecimale.

Ad esempio, U + 1F615 è il codice ufficiale Unicode Consortium per la "faccia confusa" 😕

Come spesso sono confuso, ho una forte affinità per questo simbolo.

La rappresentazione U + 1F615 mi confonde perché pensavo che le uniche codifiche possibili per i caratteri unicode richiedessero 8, 16, 24 o 32 bit, mentre 5 cifre esadecimali richiedevano 5x4 = 20 bit.

Ho scoperto che questo simbolo sembra essere rappresentato da una stringa esadecimale completamente diversa in bash:

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

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

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

Mi sarei aspettato che U + 1F615 si convertisse in qualcosa come \ x00 \ x01 \ xF6 \ x15 .

Non vedo la relazione tra queste 2 codifiche?

Quando cerco un simbolo nell'elenco ufficiale del consorzio Unicode , vorrei poter utilizzare quel codice direttamente senza doverlo convertire manualmente in questo modo noioso. vale a dire

  • trovare il simbolo su qualche pagina web
  • copiandolo negli appunti del browser web
  • incollandolo in bash per riecheggiare attraverso un hexdump per scoprire il codice REALE.

Posso usare questo codice a 20 bit per determinare qual è il codice a 32 bit?

Esiste una relazione tra questi 2 numeri?

Risposte:


20

UTF-8è una codifica a lunghezza variabile di Unicode. È progettato per essere superset di ASCII. Vedi Wikipedia per i dettagli della codifica. \x00 \x01 \xF6 \x15sarebbe UCS-4BEo UTF-32BEcodifica.

Per passare dal punto di codice Unicode alla codifica UTF-8, supponendo che la charmap della locale sia UTF-8 (vedi l'output di locale charmap), è solo:

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

Quest'ultimo sarà nella prossima versione dello standard POSIX .

Per quanto ne so, che la sintassi è stato introdotto nel 2000 dal autonomo GNU printfutilità (in contrasto con l' printfutilità della shell GNU), portato a echo/ printf/ $'...'builtins prima da zshnel 2003 , ksh93 nel 2004, bash nel 2010 (anche se non funziona correttamente ci fino al 2014 ), ma è stato ovviamente ispirato da altre lingue.

ksh93lo supporta anche come printf '\x1f615\n'e printf '\u{1f615}\n'.

$'\uXXXX'e $'\UXXXXXXXX'sono supportati da zsh, bash, ksh93, mkshe FreeBSD sh, GNU printf, GNU echo.

Alcuni richiedono tutte le cifre (in \U0001F615contrapposizione a \U1F615), sebbene ciò possa cambiare nelle versioni future poiché POSIX consentirà un numero inferiore di cifre. In ogni caso, sono necessarie tutte le cifre se \UXXXXXXXXdevono essere seguite da cifre esadecimali come in \U0001F615FOX, come \U1F615FOXsarebbe stato $'\U001F615F'OX.

Alcuni si espandono ai caratteri nella codifica della locale corrente al momento dell'analisi della stringa o al momento della sua espansione, altri solo in UTF-8, indipendentemente dalla locale. Se il carattere non è disponibile nella codifica della locale corrente, il comportamento varia tra le shell.

Quindi, per una migliore portabilità, la cosa migliore è usarla solo in locali UTF-8 e usare tutte le cifre, e usarla in $'...':

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

Nota che:

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

o:

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

Non funzionerà con tutte le shell (incluso bash) perché $'\U0001F615'viene analizzato prima che LC_ALLsia assegnato. (nota anche che non esiste alcuna garanzia che un sistema abbia una locale chiamata C.UTF-8)

Avresti bisogno di:

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

O:

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

(non all'interno di un comando o una funzione composti).


Per il contrario, per passare dalla codifica UTF-8 al punto di codice Unicode, vedere questa altra domanda o quella .

$ 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
Si noti che se \U1F615è seguito da un'altra cifra esadecimale valida, si supporrà che faccia parte della sequenza di escape. Per farlo funzionare indipendentemente da ciò che viene seguito, deve avere abbastanza zeri \U0001F615
iniziali

@kasperd, grazie. Sì, vale la pena notare. L'ho incluso nella risposta.
Stéphane Chazelas,

7

Ecco un modo per convertire da UTF-32 (big endian) a UTF-8

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

Noterai il tuo valore esadecimale 0x01F615lì, imbottito con uno 0 in più per riempire 32 bit.

La pagina Wikipedia su UTF-8 spiega chiaramente la trasformazione da un punto di codice Unicode alla sua rappresentazione UTF-8. Ma provare a farlo da soli con gli script di shell potrebbe non essere la migliore idea.

UTF-32 è a larghezza fissa e la corrispondenza tra punti di codice e rappresentazione UTF-32 è banale - il valore è lo stesso.


6

Bel modo di farlo nella tua testa o sulla carta:

  1. Scopri quanti byte saranno: i valori sotto U + 0080 sono un byte, altrimenti sotto U + 0800 sono 2 byte, altrimenti sotto U + 10000 sono 3 byte, altrimenti 4 byte. Nel tuo caso, 4 byte.

  2. Converti esadecimale a ottale: 0373025.

  3. A partire dalla fine, staccare 2 cifre ottali alla volta per ottenere una sequenza di valori ottali: 037 030 025.

  4. Se si dispone di un numero inferiore di valori ottali rispetto al numero atteso di byte, aggiungere un ulteriore 0 all'inizio: 000 037 030 025.

  5. Per tutti, ma il primo, add on 0200per ottenere: 000 0237 0230 0225.

  6. Per il primo, aggiungere 0300se la durata prevista è di 2, 0340se si tratta di 3, o 0360se si tratta di 4, per ottenere: 360 0237 0230 0225.

Ora scrivere come una serie di fughe ottale: \360\237\230\225. Se lo desideri, converti nuovamente in esadecimale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.