如何将U + xxxxx代码指定的表情转换为utf-8?


16

表情符号似乎是使用U + xxxxx的格式指定的,
其中每个x是一个十六进制数字。

例如,U + 1F615是“困惑的面孔” 的正式Unicode联盟代码code

由于我经常感到困惑,所以我对这个符号有很强的亲和力。

U + 1F615表示是混淆我,因为我认为有可能为Unicode字符的唯一编码所需的8,16,24或32位,而5个十六进制数字需要5×4 = 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联盟列表中查找符号时,我希望能够直接使用该代码,而不必以这种乏味的方式手动将其转换。即

  • 在某些网页上找到符号
  • 将其复制到Web浏览器的剪贴板
  • 将其粘贴到bash中以通过十六进制转储进行回显以发现REAL代码。

我可以使用此20位代码来确定32位代码是什么吗?

这两个数字之间是否存在关系?

Answers:


20

UTF-8是Unicode 的可变长度编码。它被设计为ASCII的超集。有关编码的详细信息,请参见Wikipedia\x00 \x01 \xF6 \x15将是UCS-4BEUTF-32BE编码。

为了从Unicode代码点转换为UTF-8编码,假设语言环境的超级用户名是UTF-8(请参阅参考资料的输出locale charmap),它只是:

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

后者将在POSIX标准的下一版本中

据我所知,这句法是在2000年由独立的GNU介绍printf实用程序(而不是在printfGNU的外壳的实用程序),提请echo/ printf/ $'...'内建首先通过zsh在2003年,ksh93的2004年,庆典在2010年(虽然不能正常工作有直到2014年),但显然受到其他语言的启发。

ksh93也支持printf '\x1f615\n'printf '\u{1f615}\n'

$'\uXXXX'$'\UXXXXXXXX'通过支持zshbashksh93mksh和FreeBSD sh,GNU printf,GNU echo

尽管POSIX会允许更少的数字,但某些版本要求所有数字(与\U0001F615相对应\U1F615),但在以后的版本中可能会更改。无论如何,您都需要所有数字(如果\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 2015年

@kasperd,谢谢。是的,值得注意。我已将其包含在答案中。
斯特凡Chazelas

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上的Wikipedia页面非常清楚地说明了从Unicode代码点到其UTF-8表示形式的转换。但是,尝试自己在Shell脚本中完成操作可能不是最好的主意。

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. 对于除第一个以外的所有内容,添加以下内容0200000 0237 0230 0225

  6. 对于第一个,加上0300如果预期的长度为2,0340如果是3,或者0360如果是4,得到:360 0237 0230 0225

现在编写为八进制转义字符串:\360\237\230\225。如果需要,可以选择转换回十六进制。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.