¿Cómo convertir un emoticón especificado por un código U + xxxxx a utf-8?


16

Los emoticones parecen estar especificados usando un formato de U + xxxxx
en el que cada x es un dígito hexadecimal.

Por ejemplo, U + 1F615 es el código oficial del Consorcio Unicode para la "cara confundida" 😕

Como a menudo estoy confundido, tengo una fuerte afinidad por este símbolo.

El U + 1F615 representación de me resulta confusa porque pensé que las únicas codificaciones posibles para caracteres unicode requerían 8, 16, 24 o 32 bits, mientras que 5 dígitos hexadecimales requieren 5x4 = 20 bits.

Descubrí que este símbolo parece estar representado por una cadena hexadecimal completamente diferente en bash:

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

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

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

Hubiera esperado que U + 1F615 se convirtiera en algo así como \ x00 \ x01 \ xF6 \ x15 .

¿No veo la relación entre estas 2 codificaciones?

Cuando busco un símbolo en la lista oficial del Consorcio Unicode , me gustaría poder usar ese código directamente sin tener que convertirlo manualmente de esta manera tediosa. es decir

  • encontrar el símbolo en alguna página web
  • copiarlo al portapapeles del navegador web
  • pegándolo en bash para hacer eco a través de un hexdump para descubrir el código REAL.

¿Puedo usar este código de 20 bits para determinar cuál es el código de 32 bits?

¿Existe una relación entre estos 2 números?

Respuestas:


20

UTF-8es una codificación de longitud variable de Unicode. Está diseñado para ser un superconjunto de ASCII. Ver Wikipedia para detalles de la codificación. \x00 \x01 \xF6 \x15sería UCS-4BEo UTF-32BEcodificación.

Para pasar del punto de código Unicode a la codificación UTF-8, suponiendo que el mapa de localización de la configuración regional sea UTF-8 (vea la salida de locale charmap), es solo:

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

Este último estará en la próxima versión del estándar POSIX .

Que yo sepa, que la sintaxis se introdujo en 2000 por el autónomo GNU printfutilidad (en contraposición a la printfutilidad de la cáscara de GNU), traído a echo/ printf/ $'...'órdenes internas primero por zshen 2003 , ksh93 en 2004, golpe del en 2010 (aunque no funciona correctamente existe hasta 2014 ), pero obviamente se inspiró en otros idiomas.

ksh93También lo admite como printf '\x1f615\n'y printf '\u{1f615}\n'.

$'\uXXXX'y $'\UXXXXXXXX'están apoyados por zsh, bash, ksh93, mkshy FreeBSD sh, GNU printf, GNU echo.

Algunos requieren todos los dígitos (a \U0001F615diferencia de \U1F615), aunque es probable que eso cambie en futuras versiones, ya que POSIX permitirá menos dígitos. En cualquier caso, necesita todos los dígitos si \UXXXXXXXXva a ser seguido por dígitos hexadecimales como en \U0001F615FOX, como \U1F615FOXhabría sido $'\U001F615F'OX.

Algunos se expanden a los caracteres en la codificación del entorno local actual en el momento en que se analiza la cadena o en el momento en que se expande, algunos solo en UTF-8, independientemente del entorno local. Si el carácter no está disponible en la codificación del entorno local actual, el comportamiento varía entre shells.

Entonces, para una mejor portabilidad, lo mejor es usarlo solo en configuraciones regionales UTF-8 y usar todos los dígitos, y usarlo en $'...':

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

Tenga en cuenta que:

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

o:

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

No funcionará con todos los shells (incluidos bash) porque $'\U0001F615'se ha analizado antes LC_ALL. (también tenga en cuenta que no hay garantía de que un sistema tenga una configuración regional llamada C.UTF-8)

Necesitarías

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

O:

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

(no dentro de un comando o función compuesta).


Para el reverso, para pasar de la codificación UTF-8 al punto de código Unicode, vea esta otra pregunta o esa .

$ 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
Tenga en cuenta que si \U1F615le sigue otro dígito hexadecimal válido, se supondrá que forma parte de la secuencia de escape. Para que funcione independientemente de lo que le sigue tiene que tener ceros a ser exactamente ocho dígitos de longitud suficiente:\U0001F615
kasperd

@kasperd, gracias. Sí, vale la pena señalar. Lo he incluido en la respuesta.
Stéphane Chazelas

7

Aquí hay una manera de convertir UTF-32 (big endian) a UTF-8

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

Notarás tu valor hexadecimal 0x01F615 allí, rellenado con un 0 inicial adicional para llenar 32 bits.

La página de Wikipedia sobre UTF-8 explica la transformación de un punto de código Unicode a su representación UTF-8 muy claramente. Pero tratar de hacerlo usted mismo en scripts de shell podría no ser la mejor idea.

UTF-32 es de ancho fijo, y la correspondencia entre el punto de código y la representación de UTF-32 es trivial: el valor es el mismo.


6

Buena manera de hacerlo en tu cabeza o en papel:

  1. Calcule cuántos bytes serán: los valores bajo U + 0080 son un byte, de lo contrario, bajo U + 0800 son 2 bytes, de lo contrario, bajo U + 10000 son 3 bytes, de lo contrario 4 bytes. En su caso, 4 bytes.

  2. Convertir a hexadecimal octal: 0373025.

  3. Comenzando en el extremo, cáscara de 2 dígitos octales a la vez para obtener una secuencia de valores octales: 037 030 025.

  4. Si tiene menos valores octales que el número esperado de bytes, añadir un extra 0 al principio: 000 037 030 025.

  5. Para todos, pero la primera, añadir el 0200de conseguir: 000 0237 0230 0225.

  6. Para el primero, añadir 0300, si la duración prevista es de 2, 0340si es 3, o 0360si es 4, para obtener: 360 0237 0230 0225.

Ahora escribe como una cadena de escapes octales: \360\237\230\225. Opcionalmente, vuelva a convertirlo en hexadecimal si lo desea.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.