ImH*_*ere 3 shell bash zsh character-encoding
在某些情况下,需要知道(使用)每个单独字符的整理顺序。它通常用正则表达式的字符类表示,如[b-d]
. 该字符类将仅匹配给定范围内的一个字符。
哪些单个字符是该范围b-d
(或其他范围)中的字符。
还知道 C 语言环境中的整理顺序是每个 ASCII 字符[a]的字节值(仅显示 33 到 126 的字符):
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
Run Code Online (Sandbox Code Playgroud)
字符范围可以扩展到 ASCII 之外吗?
但:
其他语言环境中单个字符的整理顺序是什么?
有没有办法 s?h?o?w? 这样的整理顺序(在任何语言环境中)?
[a]在使用 ASCII 的系统(大多数系统)中,但其他系统可能使用 EBCDIC 甚至其他东西。
这有几个方面。我们需要列出语言环境字符集中的所有字符,选择图形字符(例如 33 到 126 ASCII 字符)并对它们进行排序。
还有一个问题是,谈论字符的整理顺序是否有意义,或者是否曾经定义过。我将从最后一点开始。
如果我们谈论的C / POSIX排序算法由实现的strcoll()
和使用的sort
,或ls
,或壳水珠或awk
/expr
的<
,>
字符串比较操作,并且根据用户对POSIX系统区域设置的排序文本更普遍的大多数工具,笔记它们用于比较字符串。
同时在GNU系统中,一个是en_US.UTF-8语言环境é
是由一个字符串é
的字符排序后,由单个的串e
字符,Stéphane
排序前 Stephanie
。在 cs_CZ.UTF-8 语言环境中,c
在b
和之间排序d
,但ch
在h
和之间排序i
。
整理算法考虑整个字符串,而不是单独的字符。因此,知道单独比较时的字符顺序并不一定能告诉我们包含这些字符的字符串将如何比较。
该算法旨在像现实世界中的许多语言一样比较字典和电话簿中的字符串。涵盖不同文化中排序的所有微妙之处有点太简单了(请参阅ICU,它实现了不同的算法以了解更多信息),但对于大多数情况来说已经足够了。
在该算法中,整理元素,包括字符,但也包括字符的组合,如韩语字母表或捷克语的多部分字素ch
或在某些系统上é
表示为e
后跟组合重音符号 (U+0301) 被分配几个权重.
并且整个字符串依次使用每个权重进行比较,从主要权重到最后一个权重。
例如,在那个en_US.UTF-8
GNU 语言环境中,E
, é
, e
,É
都具有相同的主要权重。Stéphane
并Stephanie
分解为
<S><t><é><p><h><a><n> <e>
<S><t><e><p><h><a><n> <i><e>
Run Code Online (Sandbox Code Playgroud)
整理元素(这里,每个字符一个)。
截至n
,两个字符串的整理元素具有相同的主要权重,但i
的主要权重比 大e
,因此Stephanie
排序之后Stéphane
,甚至不必考虑次要权重。
现在,对于Stephane
vs Stéphane
,在比较主要权重时,它们的排序相同,因此必须考虑次要权重。如果您查看/usr/share/i18n/locales/iso14651_t1_common
en_US.UTF-8 语言环境按原样使用的 GNU 系统,您会看到:
<BAS> # 15
[...]
<ACA> # 18
[...]
<U0065> <e>;<BAS>;<MIN>;IGNORE # 259 e
<U00E9> <e>;<ACA>;<MIN>;IGNORE # 260 é
Run Code Online (Sandbox Code Playgroud)
对于拉丁字母表中的字符,次要权重用于比较变音符号。基本字符 ( BAS
) 排在带有重音符号 ( ACA
) 的字符之前。所以Stéphane
排序之后Stephane
。为了比较STÉPHANE
反对Stéphane
,我们不得不去到第三重其中大写字母排序在英文小写(与爱沙尼亚例如)之后。
还有一些非字母数字字符,如空格或标点符号,其主要权重在IGNORE
第一次比较中不考虑(de facto
在deface
and之间排序degree
,这并不意味着空格在f
and之间排序g
)。
对于$'STE\u0301HANE'
vs Stéphane
,某些系统(如 Solaris )会将E\u0301
除最后一个É
字符(U+00C9)字符以外的权重相同的整理元素视为\u0301
标点符号,而其他一些系统则将其视为标点符号,从而产生不那么好的结果(如$'STE\u0301HANE'
之前的Stephane
)。
在 GNU 系统上,甚至没有定义 U+0301 的排序顺序,在这种情况下还有数千个字符。我喜欢以四舍五入的数字 (U+2460..U+2473) 为例,因为这些数字显然应该有一个排序顺序,但不要¹:
$ touch ? ? ? ? ?
$ ls
? ? ? ? ?
$ ls | sort -u
?
Run Code Online (Sandbox Code Playgroud)
还有一些字符实际上被定义为与其他字符具有完全相同的权重(例如 ?
, ?
,?
这里的排序都相同)。
出于这个原因,实际上不可能在某些语言环境中对任意字符进行排序,除非像某些sort
实现那样(这将是 POSIX 规范的下一个主要版本中的要求),您退回到memcmp()
字符的类似比较那排序一样。
不同的语言环境可能使用不同的字符集。
字符集主要分为三类,单字节字符集,如 ASCII 或 iso-8859-x,其中每个字节对应一个字符(尽管有些可能未定义),多字节字符集(和编码),如 UTF-8、GB18030 、BIG5 或 EUCJP,其中字符在不同数量的字节上编码,以及有状态的,其中一个字节或字节序列可能表示不同的字符,具体取决于之前是否已发布状态转换代码。
最后一个类别现在很少在语言环境中使用,并且通常无法管理,因此我们现在可以忽略它。
C 语言环境本身保证具有单字节字符集。它不必是 ASCII,尽管它通常在它不是基于 EBCDIC 的系统上。
请注意,某些脚本(如英语中使用的拉丁文)是从左到右书写的,而其他一些是从右到左书写的,因此将这些不同脚本的字符(由某些字符集支持)放在同一行不一定是一个好主意。
组合字符也是如此,这些字符最终会被组合成随机字符并组合在一起。
另请注意,一些字符集(如 Unicode)仍在不断发展。虽然它现在固定为 0..0xD7FF、0xE000..0x10FFFF 代码点范围,但其中大部分仍未分配,每个新版本的 Unicode 都会分配新的,系统供应商试图跟上。
graph
ISO/IEC 30112 技术报告 (2014) 列出了分类为 的字符,该报告遵循 ISO/IEC TR 14652 (2002)。GNU 语言环境似乎遵循这一点,而其他一些(如 FreeBSD/Solaris)则没有,但我不会责怪他们,因为这对我来说似乎没有多大意义。例如,它排除大多数间距字符,但不排除 U+00A0(不间断空格)、U+2007(图形空格)和 U+200B(零宽度空格)。它包括我认为是控制字符的字符,例如 U+200C..U+200F、U+202D、U+202E...² 后者,从右到左的覆盖对于此问答至关重要,因为它反转从左到右字符的顺序:
$ printf '%b\n' '\u202E' a b c | sort | paste -sd '\0' -
?abc
Run Code Online (Sandbox Code Playgroud)
(有些浏览器会显示cba
他们是否支持它,其他浏览器abc
)。
它还包括大多数标签字符以及数以千计的私人使用字符,这些字符不太可能被分配,更不用说可在您的系统上绘制了。
对于单字节字符集(在 GNU 系统上,那些locale ctype-mb-cur-max
返回 1 的字符集),列出图形字符应该只是遍历所有 255 个字节值的问题(省略第一个,每个字符集中的 NUL 不是图形并且会导致问题)并将它们与[[:graph:]]
.
awk
例如,可以这样做:
awk '
BEGIN{
for (i = 1; i < 256; i++) {
c = sprintf("%c", i)
if (c ~ /[[:graph:]]/) print c
}
}' | sort | paste -sd '\0' -
Run Code Online (Sandbox Code Playgroud)
在el_GR.iso88597
语言环境中,希腊语使用 iso8859-7 单字节字符集,这将给出:
`^¨~<=>¦°_-,;:!?/.·'??"«»()[]{}§©@$£¤¥*\&#%+±ª???0½12²3³456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ?????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)
(尾随不间断空格被 GNU 语言环境错误分类为“图形”)。
这种方法不能用于多字节字符。
如果您iconv
支持 aUCS-4BE
或UTF32BE
charset 编码,则可以将所有 unicode 代码点生成为 32 位大端数字并将它们转换为语言环境的字符集:
perl -e 'print pack("L>*", $_, 10) for 1..0xd7ff, 0xe000..0x10ffff' |
iconv -cf UCS-4BE |
grep '[[:graph:]]' |
sort
Run Code Online (Sandbox Code Playgroud)
或者,如果它支持 UTF-8:
perl -C -M-warnings=nonchar -le 'print chr$_ for 1..0xd7ff, 0xe000..0x10ffff' |
iconv -cf UTF-8 |
grep '[[:graph:]]' |
sort
Run Code Online (Sandbox Code Playgroud)
(每行留一个字符以避免上述问题并避免生成太长的行)。
这适用于 Unicode(及其编码)旨在包含所有其他可能字符集的字符的事实,因此任何字符集中的每个字符总是有一个 Unicode 代码点。现代系统实际上定义了与 Unicode 相关的字符集,并且它们wchar_t
通常对应于 Unicode 代码点。
现在,如上所述, sortmemcmp()
对使用strcoll()
. 对于单字节字符集,这将是对这些字符集中的代码点进行排序;对于 UTF-8,它将在 Unicode 代码点上排序,因为 UTF-8 具有该特定属性。对于其他 Unicode 编码(如中文 GB18030 或其他多字节字符集),这可能或多或少看起来是随机的。
在任何情况下,这都意味着对于具有相同排序规则的两个语言环境,sort
如果这些语言环境使用不同的字符集,则输出将不同。
例如,如果我们回到我们的 ???????????????????????? 四舍五入的数字。Unicode 以该顺序指定它们(代码点 0x2460 到 0x2473)。在 GNU 语言环境中,它们的顺序没有定义(? 既不在 ? 之前也不在 ? 之后)。排序会放吗?后 ?在使用 UTF-8 的语言环境中,因为 UTF-8 顺序遵循 Unicode 代码点顺序。但是在像 zh_CN.gb18030 这样使用 GB18030(来自中国的另一种 Unicode 编码)的语言环境中,顺序变为 ???????????????????????,直至这些字符的编码方式在字节级别(或者至少是如果不是因为这个错误使它们订购为 ?????????????????????? )。
如果您想根据排序规则对字符串的字符进行排序,请使用zsh
:
printf "%s\n" ${(j::)${(s::o)string}}
Run Code Online (Sandbox Code Playgroud)
请注意, zsh 的变量中可以包含 NUL 字符,但strcoll()
不适用于这些字符。zsh
试图解决这个问题,但它并不完美。
如果字符串包含具有相同排序顺序的不同字符,则结果将是不确定的。
¹ 2019 年编辑? ? ? ? ? 此后已在较新版本的 GNU libc 中修复,但截至 2.30,超过 95% 的字符仍然没有定义的顺序,您可以替换 ? ? ? ? ? 例如。希望 GNU 语言环境最终会被完全修复(如果他们想遵守标准的下一个修订版,他们将不得不这样做),然后问题将仅限于用户定义的语言环境
² 我认为理由是不间断空格、图形空格、零宽度空格等不包括在space
类别中,理由是它们不应用作分隔符(ISO30112 将它们定义为归类为空白的字符字符,找到句法边界),并graph
定义为非间距可打印字符(属于print
类别而非space
类别的字符,尽管 ISO30112 的文本将其定义为归类为可打印字符的字符,不包括<空格> 字符)。所以实际上,这是不用作句法边界的图形字符和可打印的非图形字符。
归档时间: |
|
查看次数: |
1083 次 |
最近记录: |