我正在尝试编写一个严格符合 POSIX 标准的 shell,但标准没有明确说明如何从字节转换为字符。它说使用,这进一步链接到charmap 文件LC_CTYPE
的概念,但没有任何地方说明这些 charmap 文件位于何处以及如何读取它们。说它们通常位于,但这对我来说还不够。此外,在我的 Linux 系统上,该目录中的文件是 gzip 编码的,我认为这可能不符合标准,但我没有在任何地方找到有关它的任何信息。man 5 charmap
/usr/share/i18n/charmaps
我认为一定有一些标准的 C 实用程序可以获取当前字符编码的详细信息(否则怎么会有人使用它?),但我在网络搜索中找不到任何此类信息。有和函数setlocale
,nl_langinfo
后者可以为您提供当前使用的字符编码的名称,但这对我解码它没有帮助。
或者我应该提前知道并实现所有这些?
一旦完成了
setlocale()
,MB_CUR_MAX
将会告诉您是否有多字节字符集。如果是多字节,则可以使用
mb*()
POSIX 函数mbstowcs()
将多字节字符串转换为宽字符。然后使用
isblank()
/isalpha()
.. vsiswblank()
/iswalpha()
例如,如果您需要知道一个字符是空白还是字母... 请参阅iconv()
在字符集之间进行转换。所有wc*()
函数都适用于宽字符(请注意,并非所有系统都使用 Unicode 代码点作为 wchar_t 值)。shell 本身语法中使用的字符应该是可移植字符集 (目前实践中为 ASCII) 的一部分,因此在实践中跨语言环境保持不变¹。
不过,shell 所做的很多工作都是在字节级别进行的,因此在很多情况下,您无需解码即可解决问题。还有一个问题是,对于无法解码为字符的字符串该怎么办。命令行参数不必是文本,也可以是各种不同字符集的文本,因此 shell 应该能够处理这种情况。它们还应该能够处理中途的区域设置更改,例如在 之后
export LC_CTYPE=something-else
。对于今天编写的 shell,我认为我只会支持 UTF-8 作为多字节字符编码,就像
mksh
所做的那样-o utf8-mode
,许多其他多字节字符编码都很危险,尤其是在 shell 中,所以最好还是避免使用。然后你可以不用所有的mb*
API,手动进行 UTF-8 解码/编码。您可能想看看其他 shell 的表现如何,这里不是提供完整指南的地方。您会发现它通常不太好。例如,bash 的模式匹配 wrt 多字节处理非常糟糕,我不建议模仿。当您必须对字符串执行文本操作(如模式匹配、剪切、字符串长度……)时,将无法解码为字符的字节解码为特殊的 wchar_t 值(如 zsh/python 所做的)可能是处理非文本的最佳方法。
您可能需要查看具有一定程度的多字节支持的 Shell,包括 bash、zsh、ksh93、mksh、bosh 和 yash。yash 会阻塞非文本。
¹ 尽管 POSIX 建议,例如,任何在语言环境中被视为空白的字符都可以用作语法中的分隔符,或者变量名可以有任何数字,但我强烈建议不要这样做。很少有 shell 这样做,或者只部分这样做,这只会让生活变得更加困难,并为用户提供无法可靠的 API。请注意,某些系统仍然具有以 ms-kangi 为字符集的语言环境。该字符集没有反斜杠字符,并且字节 0x5c 是日元 (¥) 符号。您通常可以摆脱它,因为包括 shell 在内的工具实际上并不会将字节解码为字符,尤其是在单字节语言环境中,因此 ¥ 通常可以代替那里使用,
\
因为工具只是将反斜杠含义附加到 0x5c 字节。