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

Ale*_*yan 19 shell character-encoding unicode

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

例如,U+1F615是“困惑的脸”的官方 Unicode Consortium 代码

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

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  >'
  >
Run Code Online (Sandbox Code Playgroud)

我本来希望U+1F615转换为类似\x00 \x01 \xF6 \x15 的东西

我没有看到这两种编码之间的关系?

当我在官方 Unicode Consortium 列表中查找符号时,我希望能够直接使用该代码,而不必以这种繁琐的方式手动转换它。IE

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

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

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

Sté*_*las 22

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

要从 Unicode 代码点到 UTF-8 编码,假设语言环境的魅力图是 UTF-8(参见 的输出locale charmap),它只是:

$ printf '\U1F615\n'

$ echo -e '\U1F615'

$ confused_face=$'\U1F615'
Run Code Online (Sandbox Code Playgroud)

后者将出现在下一版本的 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

有些需要所有数字(与\U0001F615相对\U1F615),但在未来版本中可能会改变,因为 POSIX 将允许更少的数字。在任何情况下,你需要所有的数字,如果\UXXXXXXXX是应遵循的十六进制数字为\U0001F615FOX,因为\U1F615FOX本来$'\U001F615F'OX

有些在解析字符串时或在扩展字符串时扩展为当前语言环境编码中的字符,有些仅在 UTF-8 中而不考虑语言环境。如果字符在当前语言环境的编码中不可用,则行为因外壳而异。

因此,为了获得最佳可移植性,最好仅在 UTF-8 语言环境中使用它并使用所有数字,并在$'...'以下情况下使用它:

printf '%s\n' $'\U0001F615'
Run Code Online (Sandbox Code Playgroud)

注意:

LC_ALL=C.UTF-8; printf '%s\n' $'\U0001F615'
Run Code Online (Sandbox Code Playgroud)

或者:

{
  LC_ALL=C.UTF-8
  printf '%s\n' $'\U0001F615'
}
Run Code Online (Sandbox Code Playgroud)

不会与所有的炮弹(包括工作bash),因为$'\U0001F615'解析之前LC_ALL被分配。(另请注意,不能保证系统将具有名为 的区域设置C.UTF-8

你需要:

LC_ALL=C.UTF-8; eval "confused_face=$'\U0001F615'"
Run Code Online (Sandbox Code Playgroud)

或者:

LC_ALL=C.UTF-8
printf '%s\n' $'\U0001F615'
Run Code Online (Sandbox Code Playgroud)

(不在复合命令或函数中)。


相反,要从 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
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果 `\U1F615` 后跟另一个有效的十六进制数字,那么它将被假定为转义序列的一部分。无论后面跟着什么,为了让它工作,它必须有足够的前导零,正好是八位数字:`\U0001F615` (2认同)

Mat*_*Mat 7

这是一种从 UTF-32(大端)转换为 UTF-8 的方法

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

Run Code Online (Sandbox Code Playgroud)

你会注意到你的十六进制值0x01F615,用额外的前导 0 填充以填充 32 位。

关于UTF-8的维基百科页面非常清楚地解释了从 Unicode 代码点到其 UTF-8 表示的转换。但是尝试在 shell 脚本中自己做这件事可能不是最好的主意。

UTF-32是固定宽度的,代码点和 UTF-32 表示之间的对应关系是微不足道的——值是一样的。


R..*_*ICE 6

在头脑中或在纸上做这件事的好方法:

  1. 算出有多少字节:U+0080 下的值为 1 个字节,否则 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. 如果需要,可以选择转换回十六进制。