源自这讨论:
当我有(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='
答案1
在 POSIX 兼容 shell(不是 Bourne shell,该功能来自 Korn shell)中,${#var}
likewc -m
计算人物¹ in$var
并且如果存储在中的字节序列$var
无法解码为当前语言环境中的字符,则行为未指定。
根据当前区域设置(其LC_CTYPE
类别)将字节解码为字符。在使用 UTF-8 作为字符编码的语言环境中,0xc3 0xa9 序列将被解码为字符,而在使用 ISO8859-1 的语言环境中,该序列é
将被解码为.é
矇
无论如何,它与 Unicode 代码点关系不大。它也不同于计算终端或任何其他显示设备显示时的字素簇数量或字符串宽度。
在:
var="e\xcc\x81"
$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
var=$'e\xcc\x81'
或者你总是可以这样做:
var=$(printf 'e\314\201')
locale charmap
然后,在输出的语言环境中UTF-8
,$var
将包含 3 个字节(如 所示wc -c
)、2 个字符(如wc -m
或所示${#var}
)、1 个字素簇(如 GNU 所示grep -Po '\X'
),通常以宽度 1 显示(如 GNU 所示wc -L
)。
如果调用 shell 时以及解析和执行代码时的语言环境将 UTF-8 作为字符集,则在多个 shell 中,您还可以执行以下操作:
var=$'e\u0301'
用于包含和 U+0301(组合锐音符号)字符$var
的 UTF-8 编码。e
如果区域设置的字符集不是 UTF-8,则 shell 之间的行为会有所不同。此外,在将 Unicode 代码点扩展为字符时考虑的是解析代码时有效的语言环境还是执行代码时有效的语言环境,这取决于 shell。如果该角色不存在于区域设置的魅力图中,您还会发现行为的变化。
在 Bourne shell 中,要获取字符串的字符长度,您必须求助于其他实用程序,例如:
length=`expr "x$var" : '.*' - 1` || :
或者:
length=`printf %s "$var" | wc -m`
不过,如果您发现一个足够旧的系统仍然具有 Bourne shell,则很可能它wc
不支持-m
或者不会有printf
命令。
1 POSIX 本身不指定字节序列和字符序列之间的映射,即使在 POSIX 语言环境中也不指定,仅使用一些 API 来定义和检索该映射或将字节序列转换为字符序列 ( wchar_t
)。系统通常使用字符集标准字符集,例如 UTF-8,这是另一个 ISO 标准(ISO/IEC 10646 又名 Unicode)定义的字符集转换格式。某些系统(例如 GNU 系统)实际上使用 Unicode 代码点作为wchar_t
值,而不管区域设置如何。