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

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相对于标准系统区域设置搜索路径解析的区域设置,因此允许使用任何文件作为区域设置定义。

我可以创建一个恶意的语言环境,例如与大多数之外的字符,每个字符的字符集的一个字节sh和其他几个人被认为是空白,并且会使bash运行sh在分析一个典型的Debian的/etc/bash.bashrc文件(可用于工作再上一个shell访问git例如提供的托管服务器bash用作git服务器用户的登录外壳,ssh服务器接受LC_*/LANG变量并且攻击者可以将文件上传到服务器)。

现在,如果我发现了一个LC_CTYPE在(编译语言环境定义)/tmp/evil,我怎么会发现它是一个流氓一个以何种方式。

所以我的目标是取消编译这些语言环境定义,如果没有,至少知道哪个字符(连同它们的编码)在给定的字符类中。

所以考虑到这一点:

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

mik*_*erv 8

可能的最终解决方案

所以我已经获取了以下所有信息并提出了这个:

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)

程序员的API

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

字符集名称被省略或留空时,将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);
}
Run Code Online (Sandbox Code Playgroud)

关于POSIX字符类,您已经注意到您使用CAPI 来查找这些字符类。对于 unicode 字符和类,您可以使用recode's dump-with-names字符集来获得所需的输出。再次从它的手册

例如,该命令recode l2..full < input意味着从Latin-2UCS-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_codesetconst 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我很想知道我不会让它们工作的我渴望滚动。


Sté*_*las 3

至少在 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 之间的代码点来说是假的,并且不使用系统的转换库。