Sté*_*las 20 character-encoding locale
有什么方法可以检索当前语言环境中给定字符类(如blank, alpha, digit...)中所有字符的列表。
例如,
LC_ALL=en_GB.UTF-8 that-command blank
Run Code Online (Sandbox Code Playgroud)
理想情况下,在我的 Debian 系统上,会显示如下内容:
09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE
e1 9a 80 U+1680 OGHAM SPACE MARK
e1 a0 8e U+180E MONGOLIAN VOWEL SEPARATOR
e2 80 80 U+2000 EN QUAD
e2 80 81 U+2001 EM QUAD
e2 80 82 U+2002 EN SPACE
e2 80 83 U+2003 EM SPACE
e2 80 84 U+2004 THREE-PER-EM SPACE
e2 80 85 U+2005 FOUR-PER-EM SPACE
e2 80 86 U+2006 SIX-PER-EM SPACE
e2 80 88 U+2008 PUNCTUATION SPACE
e2 80 89 U+2009 THIN SPACE
e2 80 8a U+200A HAIR SPACE
e2 81 9f U+205F MEDIUM MATHEMATICAL SPACE
e3 80 80 U+3000 IDEOGRAPHIC SPACE
Run Code Online (Sandbox Code Playgroud)
在 C 语言环境中可以显示如下内容:
09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE
Run Code Online (Sandbox Code Playgroud)
也就是说,语言环境中字符以字节数组的形式表示(如第一个示例中的 UTF-8,第二个示例中的单字节)、等效的 Unicode 字符代码点和描述。
(编辑)既然漏洞早已被修补和披露,我可以添加一些上下文。
我在调查CVE 2014-0475时问了这个问题。glibc有一个错误,它允许用户使用LC_ALL=../../../../tmp/evil-locale相对于标准系统区域设置搜索路径解析的区域设置,因此允许使用任何文件作为区域设置定义。
我可以创建一个恶意的语言环境,例如与大多数之外的字符,每个字符的字符集的一个字节s,h和其他几个人被认为是空白,并且会使bash运行sh在分析一个典型的Debian的/etc/bash.bashrc文件(可用于工作再上一个shell访问git例如提供的托管服务器bash用作git服务器用户的登录外壳,ssh服务器接受LC_*/LANG变量并且攻击者可以将文件上传到服务器)。
现在,如果我发现了一个LC_CTYPE在(编译语言环境定义)/tmp/evil,我怎么会发现它是一个流氓一个以何种方式。
所以我的目标是取消编译这些语言环境定义,如果没有,至少知道哪个字符(连同它们的编码)在给定的字符类中。
所以考虑到这一点:
/usr/share/i18n/localeDebian 中的语言环境定义)在我的情况下没有用。recode,python或perl执行字节/多字节到/从字符转换的工具,因为它们可能(并且实际上确实如此)以与语言环境不同的方式进行转换。所以我已经获取了以下所有信息并提出了这个:
for class in $(
locale -v LC_CTYPE |
sed 's/combin.*//;s/;/\n/g;q'
) ; do
printf "\n\t%s\n\n" $class
recode u2/test16 -q </dev/null |
tr -dc "[:$class:]" |
od -A n -t a -t o1z -w12
done
Run Code Online (Sandbox Code Playgroud)
我使用od作为上面的最终过滤器作为偏好,因为我知道我不会使用多字节字符,它不会正确处理。 recode u2..dump将生成更像问题中指定的输出并正确处理宽字符。
upper
A B C D E F G H I J K L
101 102 103 104 105 106 107 110 111 112 113 114 >ABCDEFGHIJKL<
M N O P Q R S T U V W X
115 116 117 120 121 122 123 124 125 126 127 130 >MNOPQRSTUVWX<
Y Z
131 132 >YZ<
lower
a b c d e f g h i j k l
141 142 143 144 145 146 147 150 151 152 153 154 >abcdefghijkl<
m n o p q r s t u v w x
155 156 157 160 161 162 163 164 165 166 167 170 >mnopqrstuvwx<
y z
171 172 >yz<
alpha
A B C D E F G H I J K L
101 102 103 104 105 106 107 110 111 112 113 114 >ABCDEFGHIJKL<
M N O P Q R S T U V W X
115 116 117 120 121 122 123 124 125 126 127 130 >MNOPQRSTUVWX<
Y Z a b c d e f g h i j
131 132 141 142 143 144 145 146 147 150 151 152 >YZabcdefghij<
k l m n o p q r s t u v
153 154 155 156 157 160 161 162 163 164 165 166 >klmnopqrstuv<
w x y z
167 170 171 172 >wxyz<
digit
0 1 2 3 4 5 6 7 8 9
060 061 062 063 064 065 066 067 070 071 >0123456789<
xdigit
0 1 2 3 4 5 6 7 8 9 A B
060 061 062 063 064 065 066 067 070 071 101 102 >0123456789AB<
C D E F a b c d e f
103 104 105 106 141 142 143 144 145 146 >CDEFabcdef<
space
ht nl vt ff cr sp
011 012 013 014 015 040 >..... <
print
sp ! " # $ % & ' ( ) * +
040 041 042 043 044 045 046 047 050 051 052 053 > !"#$%&'()*+<
, - . / 0 1 2 3 4 5 6 7
054 055 056 057 060 061 062 063 064 065 066 067 >,-./01234567<
8 9 : ; < = > ? @ A B C
070 071 072 073 074 075 076 077 100 101 102 103 >89:;<=>?@ABC<
D E F G H I J K L M N O
104 105 106 107 110 111 112 113 114 115 116 117 >DEFGHIJKLMNO<
P Q R S T U V W X Y Z [
120 121 122 123 124 125 126 127 130 131 132 133 >PQRSTUVWXYZ[<
\ ] ^ _ ` a b c d e f g
134 135 136 137 140 141 142 143 144 145 146 147 >\]^_`abcdefg<
h i j k l m n o p q r s
150 151 152 153 154 155 156 157 160 161 162 163 >hijklmnopqrs<
t u v w x y z { | } ~
164 165 166 167 170 171 172 173 174 175 176 >tuvwxyz{|}~<
graph
! " # $ % & ' ( ) * + ,
041 042 043 044 045 046 047 050 051 052 053 054 >!"#$%&'()*+,<
- . / 0 1 2 3 4 5 6 7 8
055 056 057 060 061 062 063 064 065 066 067 070 >-./012345678<
9 : ; < = > ? @ A B C D
071 072 073 074 075 076 077 100 101 102 103 104 >9:;<=>?@ABCD<
E F G H I J K L M N O P
105 106 107 110 111 112 113 114 115 116 117 120 >EFGHIJKLMNOP<
Q R S T U V W X Y Z [ \
121 122 123 124 125 126 127 130 131 132 133 134 >QRSTUVWXYZ[\<
] ^ _ ` a b c d e f g h
135 136 137 140 141 142 143 144 145 146 147 150 >]^_`abcdefgh<
i j k l m n o p q r s t
151 152 153 154 155 156 157 160 161 162 163 164 >ijklmnopqrst<
u v w x y z { | } ~
165 166 167 170 171 172 173 174 175 176 >uvwxyz{|}~<
blank
ht sp
011 040 >. <
cntrl
nul soh stx etx eot enq ack bel bs ht nl vt
000 001 002 003 004 005 006 007 010 011 012 013 >............<
ff cr so si dle dc1 dc2 dc3 dc4 nak syn etb
014 015 016 017 020 021 022 023 024 025 026 027 >............<
can em sub esc fs gs rs us del
030 031 032 033 034 035 036 037 177 >.........<
punct
! " # $ % & ' ( ) * + ,
041 042 043 044 045 046 047 050 051 052 053 054 >!"#$%&'()*+,<
- . / : ; < = > ? @ [ \
055 056 057 072 073 074 075 076 077 100 133 134 >-./:;<=>?@[\<
] ^ _ ` { | } ~
135 136 137 140 173 174 175 176 >]^_`{|}~<
alnum
0 1 2 3 4 5 6 7 8 9 A B
060 061 062 063 064 065 066 067 070 071 101 102 >0123456789AB<
C D E F G H I J K L M N
103 104 105 106 107 110 111 112 113 114 115 116 >CDEFGHIJKLMN<
O P Q R S T U V W X Y Z
117 120 121 122 123 124 125 126 127 130 131 132 >OPQRSTUVWXYZ<
a b c d e f g h i j k l
141 142 143 144 145 146 147 150 151 152 153 154 >abcdefghijkl<
m n o p q r s t u v w x
155 156 157 160 161 162 163 164 165 166 167 170 >mnopqrstuvwx<
y z
Run Code Online (Sandbox Code Playgroud)
正如我在下面演示的那样,recode将为您提供完整的字符映射。根据其手册,它首先根据DEFAULT_CHARSET环境变量的当前值执行此操作,否则,它会完全按照您指定的方式运行:
当字符集名称被省略或留空时,将
DEFAULT_CHARSET使用环境中变量的值。如果未定义此变量,则recode库使用当前语言环境的编码。在符合POSIX 的系统上,这取决于环境变量中的第一个非空值LC_ALL, LC_CTYPE, LANG,可以通过命令确定locale charmap.
另外值得注意的recode是它是一个api:
命名的程序
recode只是其重新编码库的应用程序。重新编码库可单独用于其他 C 程序。熟悉重新编码库的一个好方法是熟悉recode程序本身。要在安装后使用重新编码库,C 程序需要有一行:
#include <recode.h>
对于国际友好的字符串比较POSIX和C标准定义了strcoll()函数:
该
strcoll()功能应比较指向的字符串s1到字符串指向s2,都解释为适合于当前语言环境的LC_COLLATE类别。
strcoll()如果成功,该函数不应更改 errno 的设置。由于没有保留返回值来指示错误,希望检查错误情况的应用程序应该将 errno 设置为 0,然后调用
strcoll(),然后检查 errno。
这是其用法的单独位置示例:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[15];
char str2[15];
int ret;
strcpy(str1, "abc");
strcpy(str2, "ABC");
ret = strcoll(str1, str2);
if(ret > 0)
{
printf("str1 is less than str2");
}
else if(ret < 0)
{
printf("str2 is less than str1");
}
else
{
printf("str1 is equal to str2");
}
return(0);
}
Run Code Online (Sandbox Code Playgroud)
关于POSIX字符类,您已经注意到您使用CAPI 来查找这些字符类。对于 unicode 字符和类,您可以使用recode's dump-with-names字符集来获得所需的输出。再次从它的手册:
例如,该命令
recode l2..full < input意味着从Latin-2到UCS-2的必要转换,因为dump-with-names仅从UCS-2连接出。在这种情况下,recode不会在转储中显示原始的 Latin-2代码,只显示相应的UCS-2值。举一个更简单的例子,命令
echo 'Hello, world!' | recode us..dump
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
UCS2 Mne Description
0048 H latin capital letter h
0065 e latin small letter e
006C l latin small letter l
006C l latin small letter l
006F o latin small letter o
002C , comma
0020 SP space
0077 w latin small letter w
006F o latin small letter o
0072 r latin small letter r
006C l latin small letter l
0064 d latin small letter d
0021 ! exclamation mark
000A LF line feed (lf)
Run Code Online (Sandbox Code Playgroud)
描述性注释以英语和 ASCII 给出,但如果英语描述不可用而法语描述可用,则使用拉丁语 1 给出法语描述。但是,如果
LANGUAGEorLANG环境变量以字母fr开头,则当两种描述都可用时,列表首选项将变为法语。
使用与上述类似的语法并结合其包含的测试数据集,我可以获得自己的字符映射:
recode -q u8/test8..dump </dev/null
Run Code Online (Sandbox Code Playgroud)
UCS2 Mne Description
0001 SH start of heading (soh)
0002 SX start of text (stx)
0003 EX end of text (etx)
...
002B + plus sign
002C , comma
002D - hyphen-minus
...
0043 C latin capital letter c
0044 D latin capital letter d
0045 E latin capital letter e
...
006B k latin small letter k
006C l latin small letter l
006D m latin small letter m
...
007B (! left curly bracket
007C !! vertical line
007D !) right curly bracket
007E '? tilde
007F DT delete (del)
Run Code Online (Sandbox Code Playgroud)
但是对于普通字符来说,recode显然是没有必要的。这应该为您提供 128 字节字符集中所有内容的命名字符:
printf %b "$(printf \\%04o $(seq 128))" |
luit -c |
od -A n -t o1z -t a -w12
Run Code Online (Sandbox Code Playgroud)
001 002 003 004 005 006 007 010 011 012 013 014 >............<
soh stx etx eot enq ack bel bs ht nl vt ff
...
171 172 173 174 175 176 177 >yz{|}~.<
y z { | } ~ del
Run Code Online (Sandbox Code Playgroud)
当然,只表示 128 字节,但那是因为我的语言环境(无论是否使用utf-8字符集)使用ASCII字符集,仅此而已。所以这就是我得到的。如果我在没有luit过滤的情况下运行它,od会将它回滚并再次打印相同的地图\0400.
但是,上述方法存在两个主要问题。首先是系统的整理顺序 - 对于非 ASCII 语言环境,字符集的位值不仅仅是seq影响,在我看来,这可能是您试图解决的问题的核心。
好吧,GNUtr's man页面声明它将[:upper:] [:lower:]按顺序扩展类 - 但这并不是很多。
我想可以使用一些笨拙的解决方案来实现,sort但这对于后端编程 API 来说将是一个相当笨拙的工具。
recode会正确地做这件事,但前几天你似乎不太喜欢这个程序。也许今天的编辑会对它产生更友好的影响,也可能不会。
GNU 也提供了gettext函数库,它似乎至少可以在上下文中解决这个问题LC_MESSAGES:
-功能:
char * bind_textdomain_codeset(const char *domainname, const char *codeset)该
bind_textdomain_codeset函数可用于指定域domainname 的消息目录的输出字符集 。该代码集参数必须是一个有效的代码集可用于名iconv_open子功能,或一个空指针。如果codeset参数是空指针,则
bind_textdomain_codeset返回名称为domainname的域 的当前选择的代码集。如果尚未选择任何代码集,则返回 NULL 。该
bind_textdomain_codeset功能可以多次使用。如果多次使用相同的域名参数,后面的调用会覆盖前面的设置。该
bind_textdomain_codeset函数返回一个指向包含所选代码集名称的字符串的指针。该字符串在函数内部分配,用户不得更改。如果系统在执行过程中出核bind_textdomain_codeset,则返回值为NULL,并相应地设置全局变量errno。
您还可以使用本机Unicode 字符类别,它们与语言无关并完全放弃 POSIX 类,或者可能要求前者为您提供足够的信息来定义后者。
除了复杂性之外,Unicode 还带来了新的可能性。一个是每个 Unicode 字符都属于某个类别。您可以将属于“字母”类别的单个字符与
\p{L}. 您可以将不属于该类别的单个字符与\P{L}.同样,“字符”实际上意味着“Unicode 代码点”。
\p{L}匹配类别“字母”中的单个代码点。如果您的输入字符串à编码为U+0061 U+0300,则匹配时a不带重音符号。如果输入à编码为U+00E0,则它与à重音匹配。原因是代码点U+0061 (a)和U+00E0 (à)都在“字母”类别中,而U+0300在“标记”类别中。您现在应该明白为什么
\P{M}\p{M}*+相当于\X.\P{M}匹配不是组合标记的代码点,而\p{M}*+匹配零个或多个是组合标记的代码点。要匹配包含任何变音符号的字母,请使用\p{L}\p{M}*+. 最后一个正则表达式将始终匹配à,无论它是如何编码的。所有格量词确保回溯不会导致\P{M}\p{M}*+匹配没有跟随它的组合标记的非标记,这\X永远不会。
提供上述信息的同一网站还讨论了Tcl自己的符合POSIX的正则表达式实现,这可能是实现目标的另一种方式。
在最后一个解决方案中,我建议您可以查询LC_COLLATE文件本身以获得完整且有序的系统字符映射。这似乎并不容易完成,但我在编译后通过以下方式取得了一些成功,如下localedef所示:
<LC_COLLATE od -j2K -a -w2048 -v |
tail -n2 |
cut -d' ' -f$(seq -s',' 4 2 2048) |
sed 's/nul\|\\0//g;s/ */ /g;:s;
s/\([^ ]\{1,3\}\) \1/\1/;ts;
s/\(\([^ ][^ ]* *\)\{16\}\)/\1\n/g'
dc1 dc2 dc3 dc4 nak syn etb can c fs c rs c sp ! "
# $ % & ' ( ) * + , - . / 0 1 2
3 4 5 6 7 8 9 : ; < = > ? @ A B
C D E F G H I J K L M N O P Q R
S T U V W X Y Z [ \ ] ^ _ ` a b
c d e f g h i j k l m n o p q r
s t u v w x y z { | } ~ del soh stx etx
eot enq ack bel c ht c vt cr c si dle dc1 del
Run Code Online (Sandbox Code Playgroud)
诚然,它目前存在缺陷,但我希望它至少证明了这种可能性。
strings $_/en_GB
#OUTPUT
int_select "<U0030><U0030>"
...
END LC_TELEPHONE
Run Code Online (Sandbox Code Playgroud)
它确实看起来并不多,但后来我开始注意到copy整个列表中的命令。例如,上面的文件似乎copy在“en_US”中,而另一个真正的大文件似乎它们都在某种程度上共享了iso_14651_t1_common.
它相当大:
strings $_ | wc -c
#OUTPUT
431545
Run Code Online (Sandbox Code Playgroud)
这里是介绍/usr/share/i18n/locales/POSIX:
# Territory:
# Revision: 1.1
# Date: 1997-03-15
# Application: general
# Users: general
# Repertoiremap: POSIX
# Charset: ISO646:1993
# Distribution and use is free, also for
# commercial purposes.
LC_CTYPE
# The following is the POSIX Locale LC_CTYPE.
# "alpha" is by default "upper" and "lower"
# "alnum" is by definiton "alpha" and "digit"
# "print" is by default "alnum", "punct" and the <U0020> character
# "graph" is by default "alnum" and "punct"
upper <U0041>;<U0042>;<U0043>;<U0044>;<U0045>;<U0046>;<U0047>;<U0048>;\
<U0049>;<U004A>;<U004B>;<U004C>;<U004D>;<U004E>;<U004F>;
Run Code Online (Sandbox Code Playgroud)
...
你grep当然可以通过这个,但你可能只是:
recode -lf gb
Run Code Online (Sandbox Code Playgroud)
反而。你会得到这样的东西:
Dec Oct Hex UCS2 Mne BS_4730
0 000 00 0000 NU null (nul)
1 001 01 0001 SH start of heading (soh)
...
Run Code Online (Sandbox Code Playgroud)
... 和更多
还有luit终端 UTF-8pty翻译设备,我猜它可以作为没有 UTF-8 支持的 XTerms 的中间人。它处理许多开关 - 例如将所有转换的字节记录到文件或-c作为简单的|pipe过滤器。
我从来没有意识到这有这么多 - 语言环境和角色地图等等。这显然是一件大事,但我想这一切都在幕后进行。至少在我的系统上,有几百个man 3与语言环境相关的搜索的相关结果。
还有:
zcat /usr/share/i18n/charmaps/UTF-8*gz | less
CHARMAP
<U0000> /x00 NULL
<U0001> /x01 START OF HEADING
<U0002> /x02 START OF TEXT
<U0003> /x03 END OF TEXT
<U0004> /x04 END OF TRANSMISSION
<U0005> /x05 ENQUIRY
...
Run Code Online (Sandbox Code Playgroud)
这将持续很长一段时间。
这些Xlib函数一直在处理这个——luit是那个包的一部分。
这些Tcl_uni...功能也可能证明是有用的。
只是一点点的<tab>完成和man搜索,我在这个主题上学到了很多东西。
使用localedef- 您可以locales在您的I18N目录中编译。输出很时髦,而且不是特别有用 - 根本不像charmaps- 但是你可以获得原始格式,就像你在上面指定的一样:
mkdir -p dir && cd $_ ; localedef -f UTF-8 -i en_GB ./
ls -l
total 1508
drwxr-xr-x 1 mikeserv mikeserv 30 May 6 18:35 LC_MESSAGES
-rw-r--r-- 1 mikeserv mikeserv 146 May 6 18:35 LC_ADDRESS
-rw-r--r-- 1 mikeserv mikeserv 1243766 May 6 18:35 LC_COLLATE
-rw-r--r-- 1 mikeserv mikeserv 256420 May 6 18:35 LC_CTYPE
-rw-r--r-- 1 mikeserv mikeserv 376 May 6 18:35 LC_IDENTIFICATION
-rw-r--r-- 1 mikeserv mikeserv 23 May 6 18:35 LC_MEASUREMENT
-rw-r--r-- 1 mikeserv mikeserv 290 May 6 18:35 LC_MONETARY
-rw-r--r-- 1 mikeserv mikeserv 77 May 6 18:35 LC_NAME
-rw-r--r-- 1 mikeserv mikeserv 54 May 6 18:35 LC_NUMERIC
-rw-r--r-- 1 mikeserv mikeserv 34 May 6 18:35 LC_PAPER
-rw-r--r-- 1 mikeserv mikeserv 56 May 6 18:35 LC_TELEPHONE
-rw-r--r-- 1 mikeserv mikeserv 2470 May 6 18:35 LC_TIME
Run Code Online (Sandbox Code Playgroud)
然后od你可以阅读它 - 字节和字符串:
od -An -a -t u1z -w12 LC_COLLATE | less
etb dle enq sp dc3 nul nul nul T nul nul nul
23 16 5 32 19 0 0 0 84 0 0 0 >... ....T...<
...
Run Code Online (Sandbox Code Playgroud)
虽然距离赢得选美比赛还有很长的路要走,但这是可用的输出。而且od,你希望它是为好,当然是可配置的。
我想我也忘记了这些:
perl -mLocale
-- Perl module --
Locale::Codes Locale::Codes::LangFam Locale::Codes::Script_Retired
Locale::Codes::Constants Locale::Codes::LangFam_Codes Locale::Country
Locale::Codes::Country Locale::Codes::LangFam_Retired Locale::Currency
Locale::Codes::Country_Codes Locale::Codes::LangVar Locale::Language
Locale::Codes::Country_Retired Locale::Codes::LangVar_Codes Locale::Maketext
Locale::Codes::Currency Locale::Codes::LangVar_Retired Locale::Maketext::Guts
Locale::Codes::Currency_Codes Locale::Codes::Language Locale::Maketext::GutsLoader
Locale::Codes::Currency_Retired Locale::Codes::Language_Codes Locale::Maketext::Simple
Locale::Codes::LangExt Locale::Codes::Language_Retired Locale::Script
Locale::Codes::LangExt_Codes Locale::Codes::Script Locale::gettext
Locale::Codes::LangExt_Retired Locale::Codes::Script_Codes locale
Run Code Online (Sandbox Code Playgroud)
我可能忘记了它们,因为我无法让它们工作。我从不使用Perl,我猜我不知道如何正确加载模块。但是man页面看起来很不错。无论如何,有些事情告诉我,你会发现调用 Perl 模块至少比我更难。而且,这些已经在我的电脑上 - 我什至从未使用过 Perl。还有一些特别是I18N我很想知道我不会让它们工作的我渴望滚动。
至少在 GNU、FreeBSD 或 Solaris 系统上,这种强力方法是有效的:
#include <wctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
unsigned long i;
int need_init;
wctype_t type;
FILE* to_perl;
setlocale(LC_ALL,"");
if (argc != 2) {
fprintf(stderr, "Usage: %s <type>\n", (argc?argv[0] : "???"));
exit(1);
}
if (!(type = wctype(argv[1]))) {
fprintf(stderr, "Invalid type: \"%s\"\n", argv[1]);
exit(1);
}
need_init = wctomb(0, 0);
to_perl = popen("perl -Mcharnames=full -ane '"
"printf \"%17s U+%04X %s\n\", join(\" \", @F[1..$#F]),"
"$F[0], charnames::viacode($F[0])'", "w");
#ifdef SUPPORT_ROGUE_LOCALES
for(i=0; i<=0x7fffffff; i++) {
#else
for(i=0; i<=0x10ffff; i++) {
if (i == 0xd800) i = 0xe000; /* skip UTF-16 surrogates */
#endif
if (iswctype(i, type)) {
int n;
unsigned char buf[1024];
if (need_init) wctomb(0, 0);
n = wctomb(buf, i);
if (n > 0) {
int c;
fprintf(to_perl, "%lu", i);
for (c = 0; c < n; c++)
fprintf(to_perl, " %02X", buf[c]);
putc('\n', to_perl);
}
}
}
pclose(to_perl);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
虽然根据 C/POSIX,wchar_t它是一种不透明类型,与 Unicode 无关,仅保证覆盖系统区域设置支持的所有字符,但实际上,在大多数支持 Unicode 的系统中,这些值确实对应于 Unicode 代码点并且语言环境定义本身基于 Unicode。
Unicode 是所有已知字符集的超集,因此循环 Unicode 中的所有有效代码点(0 到 0xD7FF 和 0xE000 到 0x10FFFF)应该至少列出给定字符集支持的所有字符。
在这里,我们使用系统的区域设置标准 API 来检查哪些属于给定类型,并将其转换为区域设置编码中的编码形式。我们使用perl及其charnames模块只是为了从给定的 Unicode 代码点获取名称。
在使用有状态编码(例如 ISO-2022-JP)的语言环境中,我们确保从默认初始状态显示编码形式。
我还没有找到一个安装了有状态字符编码的语言环境的系统,但至少在 GNU 系统上,可以生成一些语言环境,以便可以制作出流氓语言环境(并且至少 GNU 工具在这些系统中无法正常工作)语言环境)。例如,对于使用 ISO-2022-JP 和正常ja_JP语言环境的自定义语言环境,我得到:
$ LOCPATH=$PWD LC_ALL=ja_JP.ISO-2022-JP ~/list-type blank
09 U+0009 CHARACTER TABULATION
20 U+0020 SPACE
1B 24 42 21 21 U+3000 IDEOGRAPHIC SPACE
Run Code Online (Sandbox Code Playgroud)
与之比较:
$ LC_ALL=ja_JP.eucjp ~/list-type blank
09 U+0009 CHARACTER TABULATION
20 U+0020 SPACE
A1 A1 U+3000 IDEOGRAPHIC SPACE
Run Code Online (Sandbox Code Playgroud)
在 ISO-2022-JP 中,1B 24 42序列 ( \e$B) 从 ASCII 切换到字符表示为 2(7 位)字节的状态(此处为 21 21 表示该表意空间)。而在 EUCJP 中,字节相同,但状态切换是通过翻转第 8 位 ( A1 = 21 | 0x80) 来完成的,这使其更加无状态。
这意味着在这些有状态编码中,有多种方法可以写入给定字符(例如通过插入其中几个状态切换序列),并且上面代码显示的序列只是其中之一(来自初始字符的规范序列)默认状态)。
对于正常语言环境,字符不能超出 0..0xD7FF、0xE000..0x10FFFF,而对于流氓语言环境,wchar_t 支持的范围内的任何字符都可以。例如,我可以创建一个区域设置,其中 U+DCBA 或 U+12345678 字符(或者如果允许的话将是字符)为空白。这就是为什么您想要编译该代码以-D SUPPORT_ROGUE_LOCALES覆盖这些代码,尽管这意味着扫描整个列表需要更多时间。
我无法使用 @mikeserv 的解决方案,因为recode它使用它自己的转换,不再维护,并且仅支持最大 0xFFFF 的 Unicode 字符,并且 GNUtr至少不支持多字节字符。
我无法使用@ChrisDown,因为python它没有 POSIX 字符类的接口。
我尝试了 Perl,但对于 UTF-8 以外的多字节语言环境,它对于 128 到 255 之间的代码点来说是假的,并且不使用系统的转换库。