用于检索当前语言环境中给定字符类中的字符列表的命令

用于检索当前语言环境中给定字符类中的字符列表的命令

有什么方法可以检索当前语言环境中给定字符类(例如blank,,alpha... )中的所有字符的列表。digit

例如,

LC_ALL=en_GB.UTF-8 that-command blank

理想情况下,在我的 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

在 C 语言环境中可以显示类似以下内容:

09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE

也就是说,区域设置中的字符以字节数组的形式表示(如第一个示例中的 UTF-8,第二个示例中的单个字节)、等效的 Unicode 字符代码点和描述。

语境

(编辑)既然该漏洞早已被修补和披露,我可以添加一些背景信息。

我在调查的时候就问过这个问题CVE 2014-0475glibc有一个错误,它允许用户使用相LC_ALL=../../../../tmp/evil-locale对于标准系统区域设置搜索路径解析的区域设置,从而允许使用任何文件作为区域设置定义。

我可以创建一个流氓语言环境例如,每个字符一个字节的字符集,其中大多数字符除了sh和其他一些字符都被考虑空白这将在解析典型的 Debian文件时bash运行(并且可以用于在托管服务器上获取 shell 访问权限,例如,提供用作服务器用户的登录 shell ,并且服务器接受/变量,并且攻击者可以上传文件到服务器)。sh/etc/bash.bashrcgitbashgitsshLC_*LANG

现在,如果我LC_CTYPE在 中找到了一个(已编译的语言环境定义)/tmp/evil,我将如何发现它是一个流氓定义以及以何种方式发现它。

所以我的目标是取消编译这些区域设置定义,如果没有,至少知道给定字符类中的字符(及其编码)。

因此考虑到这一点:

  • 查看区域设置源文件(区域设置定义,如/usr/share/i18n/localeDebian 中的区域设置)的解决方案在我的情况下没有用处。
  • Unicode 字符属性无关紧要。我只关心语言环境所说的内容。在 Debian 系统上,即使在两个 UTF-8 系统区域设置之间(更不用说流氓系统区域设置),类中的字符列表也可能不同。
  • 不能使用诸如recodepython或 之类的进行字节/多字节到/从字符转换的工具,因为它们可能(并且实际上确实)以与语言环境不同的方式进行转换。perl

答案1

可能的最终解决方案

因此,我收集了以下所有信息并得出了以下结论:

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

笔记:

我将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

程序员的API

正如我在下面演示的,recode将为您提供完整的角色图。根据其手册,它首先根据DEFAULT_CHARSET环境变量的当前值执行此操作,否则,它完全按照您指定的方式运行:

当一个字符集name 被省略或留空,DEFAULT_CHARSET则使用环境中变量的值。如果未定义此变量,则recode库将使用当前语言环境的编码。POSIX兼容系统,这取决于环境变量中的第一个非空值LC_ALL, LC_CTYPE, LANG,可以通过命令确定locale charmap.

另外值得注意的recode是,它是一个 API:

该程序recode只是其重新编码库的一个应用程序。重新编码库可单独用于其他 C 程序。熟悉重新编码库的一个好方法是熟悉程序recode本身。

要在安装后使用重新编码库,C 程序需要有一行:

#include <recode.h>

对于国际友好的字符串比较POSIXC标准定义了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);
}

关于POSIX字符类,您已经注意到您使用CAPI 来查找它们。对于 unicode 字符和类,您可以使用recode's 转储名称字符集以获得所需的输出。从其再次手动:

例如,该命令recode l2..full < input意味着从拉丁语2UCS-2,作为转储名称仅连接出自UCS-2。在这种情况下,recode不显示原始内容 拉丁语2dump中的代码,只有对应的UCS-2价值观。举一个更简单的例子,命令

 echo 'Hello, world!' | recode us..dump

产生以下输出:

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)

描述性注释以英语和 ASCII 给出,但如果没有英语描述,但有法语描述,则使用 Latin-1 给出法语描述。但是,如果 LANGUAGELANG环境变量以字母开头FR,那么当两种描述都可用时,列表优先级将变为法语。

使用与上面类似的语法并结合它包含测试数据集我可以通过以下方式获得我自己的字符图:

recode -q u8/test8..dump </dev/null

输出

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)

但对于普通角色来说,recode显然没有必要。这将为您提供 128 字节字符集中所有内容的命名字符:

printf %b "$(printf \\%04o $(seq 128))" | 
luit -c |
od -A n -t o1z -t a -w12

输出

 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

当然,只表示 128 字节,但那是因为我的语言环境,UTF-8无论是否有字符映射,都使用ASCII码字符集仅此而已。这就是我得到的全部。如果我在没有过滤的情况下运行它luitod会将其回滚并再次打印相同的地图\0400.

然而,上述方法有两个主要问题。首先是系统的整理顺序 - 对于非 ASCII 语言环境,字符集的位值不仅仅是影响seq,正如我认为的,这可能是您要解决的问题的核心。

好吧,GNUtr's man页面声明它将[:upper:] [:lower:]按顺序扩展类 - 但这并不是很多。

我想可以使用一些严厉的解决方案来实现,sort但这对于后端编程 API 来说将是一个相当笨重的工具。

recode会正确地完成这件事,但前几天你似乎不太喜欢这个程序。也许今天的编辑会让它看起来更友好,也可能不会。

GNU也提供了gettext函数库,似乎可以解决这个问题至少对于LC_MESSAGES上下文:

- 功能:char * bind_textdomain_codesetconst char *domainname, const char *codeset

bind_textdomain_codeset函数可用于指定域的消息目录的输出字符集 域名。这代码集参数必须是有效的代码集可用于的名称iconv_打开函数或空指针。

如果代码集参数为空指针,bind_textdomain_codeset 返回当前选中的代码集对于具有名称的域 域名。如果没有则返回NULL代码集尚未被选中。

bind_textdomain_codeset功能可以多次使用。如果多次使用相同的域名参数,则后一个调用将覆盖前一个调用所做的设置。

bind_textdomain_codeset函数返回一个指向包含所选代码集名称的字符串的指针。该字符串在函数内部分配,用户不得更改。如果在执行期间系统退出核心 bind_textdomain_codeset,则返回值为 NULL,并且相应地设置全局变量 errno。

您也可以使用本机统一码字符类别,它们是独立于语言的,并且完全放弃 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

诚然,它目前存在缺陷,但我希望它至少证明了这种可能性。

乍一看

strings $_/en_GB

#OUTPUT

int_select "<U0030><U0030>"
...
END LC_TELEPHONE

它看起来确实不多,但后来我开始注意到copy整个列表中的命令。上面的文件似乎copy“en_US”例如,他们似乎都在某种程度上共享的另一个真正的大问题是iso_14651_t1_common

它相当大:

strings $_ | wc -c

#OUTPUT
431545

这是介绍/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>;

...

grep当然可以通过这个,但你可能只是:

recode -lf gb

反而。你会得到这样的东西:

Dec  Oct Hex   UCS2  Mne  BS_4730

  0  000  00   0000  NU   null (nul)
  1  001  01   0001  SH   start of heading (soh)
...

... 和更多

还有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
...

这将持续一段时间非常好一会儿。

这些Xlib函数一直在处理这个问题 -luit是该包的一部分。

这些Tcl_uni...功能可能也很有用。

只需一点点<tab>完成和man搜索,我就在这个主题上学到了很多东西。

使用- 你可以在你的目录中localedef编译。输出很时髦,并且不是特别有用 - 根本不像- 但你可以像我一样按照上面指定的那样获得原始格式:localesI18Ncharmaps

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

然后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...<
...

虽然距离赢得选美比赛还有很长的路要走,但那是可用的输出。当然,它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

我可能忘记了它们,因为我无法让它们工作。我猜我从来没有使用过Perl,也不知道如何正确加载模块。但man页面看起来相当不错。无论如何,有件事告诉我,您会发现调用 Perl 模块至少比我稍微容易一些。而且,这些已经在我的计算机上 - 而且我什至从未使用过 Perl。还有一些值得注意的I18N是,我很渴望滚动,因为我很清楚我也不会让它们工作。

答案2

至少在 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;
}

虽然根据 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

与之比较:

$ LC_ALL=ja_JP.eucjp ~/list-type blank
       09 U+0009 CHARACTER TABULATION
       20 U+0020 SPACE
    A1 A1 U+3000 IDEOGRAPHIC SPACE

在 ISO-2022-JP 中,1B 24 42序列 ( \e$B) 从 ASCII 切换到字符表示为 2(7 位)字节的状态(此处为 21 21 表示该表意空间)。而在 EUCJP 中,字节相同,但状态切换是通过翻转第 8 位 ( A1 = 21 | 0x80) 来完成的,这使其更加无状态。

这意味着在这些有状态编码中,有多种方法可以编写给定的字符(例如通过插入其中的几个)状态切换序列),上面的代码显示的序列只是其中之一(初始默认状态的规范序列)。

对于普通语言环境,字符不能超出 0..0xD7FF、0xE000..0x10FFFF 范围,对于流氓locale,wchar_t 支持的范围内的任何字符都可以。例如,我可以创建一个区域设置,其中 U+DCBA 或 U+12345678 字符(或者如果允许的话将是字符)空白。这就是为什么您想要编译该代码以-D SUPPORT_ROGUE_LOCALES覆盖这些代码,尽管这意味着扫描整个列表需要更多时间。

我无法使用 @mikeserv 的解决方案,因为recode它使用它自己的转换,不再维护,并且仅支持最大 0xFFFF 的 Unicode 字符,并且 GNUtr至少不支持多字节字符。

我无法使用@克里斯唐的aspython没有 POSIX 字符类的接口。

我尝试了 Perl,但对于 UTF-8 以外的多字节语言环境,它对于 128 到 255 之间的代码点来说是假的,并且不使用系统的转换库。

相关内容