来自这个讨论:
当我有(zsh 5.8,bash 5.1.0)
var="ASCII"
echo "${var} has the length ${#var}, and is $(printf "%s" "$var"| wc -c) bytes long"
答案很简单:这些是 5 个字符,占 5 个字节。
现在,var=Müller
产量
Müller has the length 6, and is 7 bytes long
这表明${#}
运营商计算代码点,而不是字节。这在 POSIX中有点不清楚,他们说它计算“字符”。char
如果POSIX C 中的演员通常不是八位字节,这会更清楚。
总之:不错!不错,看到LANG==en_US.utf8
了
现在,
var='??♀️'
echo "${var} has the length ${#var}, and is $(printf "%s" "$var"| wc -c) bytes long"
??♀️ has the length 5, and is 17 bytes long
太好了,我们将“深色皮肤的美人鱼”分解为 Unicode 代码点
- 人鱼
- 深色肤色
- 零宽度连接器
- 女性
- 打印将前一个字符打印为表情符号
好的,所以我们真的在计算 Unicode 代码点!
var="e\xcc\x81"
echo "${var} has the length ${#var}, and is $(printf "%s" "$var"| wc -c) bytes long"
é has the length 9, and is 9 bytes long
(当然,我的控制台字体决定了´
组合与后面的空格,而不是前面e
的 。后者是正确的。但让我们把我的愤怒留到其他地方吧。)
嗯,这里有一个轻微的“wat”。
> printf "e\xcc\x81"|wc -c
3
> printf "%s" "${var}" |wc -c
9
> echo -n ${var} |wc -c
3
> echo "${var} has the length ${#var}, and is $(printf "%s" "$var"| wc -c) bytes long"
é has the length 9, and is 9 bytes long
> printf "%s" "${var}" |xxd
00000000: 655c 7863 635c 7838 31 e\xcc\x81
这就是我放弃的地方。
echo $var
,echo ${var}
并且echo "${var}"
所有“正确”发出三个字节。但是,echo ${#var}
告诉我它是 9 个字符。
这在哪里记录/标准化,这一切的规则是什么?
在符合 POSIX 的 shell(不是 Bourne shell,该功能来自 Korn shell)中,
${#var}
likewc -m
计算字符¹的数量,并且如果存储的字节序列无法解码为当前语言环境中的字符$var
,则行为未指定。$var
LC_CTYPE
根据当前语言环境(其类别)将字节解码为字符。在使用 UTF-8 作为字符编码的语言环境中,0xc3 0xa9 序列将被解码为一个é
字符,而在使用 ISO8859-1 的语言环境中,将被解码é
为矇
.无论如何,它与 Unicode 代码点几乎没有关系。当终端或任何其他显示设备显示时,它也与计算字素簇的数量或字符串的宽度不同。
在:
$var
包含 9 个字节和 9 个字符:e
,\
,x
,c
,c
,\
,x
,8
和1
.一些
printf
(在格式参数或%b
格式指令的参数中)和echo
实现将扩展\xcc
为 0xcc 字节,并非全部都这样做。根据 POSIX,\x
在对这些的争论中会导致未指定的行为。(在格式参数和/虽然\351
扩展为 0xe9 字节)。printf
\0351
echo
%b
如果你想在/ /
$var
中包含0x65
,0xcc
,0x81
字节(现在越来越多的 shell),你会这样做:ksh93
zsh
bash
或者你总是可以这样做:
然后在 output 的语言环境中,
locale charmap
将包含 3 个字节(如 所示)、2 个字符(如or所示)、1 个字素簇(如 GNU 所示),通常以宽度 1 显示(如 GNU 所示)。UTF-8
$var
wc -c
wc -m
${#var}
grep -Po '\X'
wc -L
如果调用 shell 时的语言环境以及在解析和执行代码时使用 UTF-8 作为字符集,在几个 shell 中,您还可以执行以下操作:
用于
$var
包含 UTF-8 编码e
和 U+0301(组合重音符号)字符。如果语言环境的字符集不是 UTF-8,那么 shell 之间的行为会有所不同。此外,将 Unicode 代码点扩展为字符时考虑到的是在解析代码时还是在执行代码时有效的语言环境取决于 shell。如果角色不在区域设置的charmap 中,您还会发现行为的变化。
在 Bourne shell 中,要获取字符串的字符长度,您必须求助于其他实用程序,例如:
或者:
虽然如果你发现一个系统足够老,仍然有一个 Bourne shell,它很
wc
可能不支持-m
或者没有printf
命令。¹ POSIX 本身没有指定字节序列和字符序列之间的映射,即使在 POSIX 语言环境中也没有,只有一些 API 来定义和检索该映射或将字节序列转换为字符序列 (
wchar_t
)。系统通常为charmap 使用标准字符集,如UTF-8,它是另一种ISO 标准(ISO/IEC 10646 aka Unicode)定义的字符集的转换格式。某些系统(如 GNU 系统)实际上使用 Unicode 代码点作为wchar_t
值,而不管区域设置如何。