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


18

有什么能检索在给定的人物类中的所有字符(如列表的方式blankalphadigit...)在当前的语言环境。

例如,

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-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/locale在我的情况下,查看语言环境的源文件(语言环境定义如Debian中的定义)的解决方案是没有用的。
  • Unicode字符属性无关紧要。我只关心语言环境的内容。在Debian系统上,即使在两个UTF-8系统语言环境之间,更不用说流氓语言环境了,类中的字符列表也可以不同。
  • 工具,如recodepythonperl该执行字节/多字节到/从字符转换不能被使用,因为它们可以(并且在实践中那样)进行转换比的区域设置方式不同。

对于大多数语言环境,它最终都来自(带有glibc)中的LC_CTYPE内容/usr/share/i18n/locales/i18n……当然,其中大部分来自Unicode字符数据库。当然,拥有一个命令会很不错
德罗伯特2014年

@derobert,是的,尽管locale(至少是GNU)检索了存储在许多类别中的许多信息,但它并不是LC_CTYPE和LC_COLLATE中最重要的信息。我想知道是否有隐藏的API来检索该信息或反编译语言环境信息。
斯特凡Chazelas

是的-您可以解析该信息-我终于结束了对我的编辑的总结。您可能已经安装了几个命令-至少我已经安装了,甚至我都不知道。希望对您有所帮助。具体来说recodeuconv可以给您您所要查找的内容。也许甚至只是luitod我猜...
mikeserv

这是非常好的!perl我认为这意味着您根本不需要。
mikeserv

我似乎能够基本上从提取我的字符集LC_CTYPE与刚刚od -A n -t c <LC_CTYPE | tsort也许你已经尝试过,但我从来没有听说过,我是通过阅读info,它让我想起了这一点-它似乎工作。也有,ptx但我认为它的意义不大。无论如何,如果您还没有尝试过并决定这样做-公平警告-确实需要一点耐心。lehman.cuny.edu/cgi-bin/man-cgi?tsort+1
mikeserv

Answers:


7

可能的最终解决方案

因此,我采用了以下所有信息并提出了以下建议:

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环境变量的当前值执行此操作,否则,它将完全按照您指定的方式运行:

当省略字符集名称或将其保留为空时,将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 dump-with-names charset获得所需的输出。再次从其手册中

例如,该命令recode l2..full < input暗示必须从Latin-2转换为UCS-2,因为dump-with-names仅从UCS-2连接在这种情况下,recode不会在转储中显示原始的 Latin-2代码,而仅显示相应的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给出法语描述。但是,如果 LANGUAGEor LANG环境变量以字母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字节字符集中的所有内容命名为chars:

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字符集,仅此而已。这就是我所得到的。如果我运行它但未对其进行luit过滤,od则会将其回滚并再次打印相同的地图,直到\0400.

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

好吧,GNU tr'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功能可以使用多次。如果使用相同的domainname参数多次使用,则后面的调用将覆盖前面的设置。

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

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

乍一看

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-8 pty转换设备,可以在没有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-您可以localesI18N目录中编译。输出是时髦的,并且不是特别有用-根本不像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

然后,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我有些不知所措,因为我非常了解我也不会让他们工作。


1
这些都是非常有用的有用信息,但是它提供了有关源文件的信息,该源文件中的信息i18n可能已经使用,也可能未用于生成我当前正在使用的语言环境。语言环境信息可能来自/usr/lib/locale/locale-archive/some/dir/LC_CTYPE,这是与我的语言环境相关部分,存储在我所追求的那些文件中。
斯特凡Chazelas

@StephaneChezales-因此只需LC_STUFF从存档中提取您的内容localedef-它也会这样做。我猜我也可以演示。您还可以查看并与其他几乎所有stringsod或任何休息。无论如何,我做到了。但到了方式- charmaps 和-目前使用的语言环境的youre localedef上会报告也是如此。也多数民众赞成在recode做什么。
mikeserv

您基本上是在说我们可以手动执行系统的库来查询字符类信息,但这将需要数千行代码来可靠地执行,并且结果将取决于系统。(以与系统库相同的方式解析环境(LOCPATH,LANG,LANGUAGE,LC_CTYPE ...,确定在何处查找数据,将其提取...)。我看不到如何从存档中提取内容
史蒂芬·夏泽拉斯

@StephaneChazelas -我不建议你用手工做的-我建议你用电脑做-使用系统二进制文件,例如od, recodeuconv和休息。但这是我的错误-不是将localedef其提取出来,而是recode会的。您必须签出info recode-除了recode我显示的table命令外,还有很多相同的事情-我认为它会以相同的方式处理事情。它不仅仅使您的字符集脱颖而出。无论如何,我对这些perl模块都寄予了厚望-您是否尝试过?
mikeserv 2014年

1
如果有一个API可以检索当前语言环境中给定字符类中的字符列表,那么这正是我要寻找的。如果您可以演示如何执行此操作,我将接受答案。我唯一想到的(以及如何获得问题中的“预期输出”)是iswblank(3)用于所有可能的字符值。
斯特凡Chazelas

1

至少在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位)字节(此处为IDEOGRAPHIC SPACE的21 21)。在EUCJP中,它是相同的字节,但是状态切换是通过翻转第8位(A1 = 21 | 0x80)来完成的,这使它变得更加无状态。

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

对于正常语言环境,字符不能在0..0xD7FF,0xE000..0x10FFFF之外,对于恶意语言环境,wchar_t支持的范围内的任何字符都可以。例如,我可以创建一个语言环境,其中U + DCBA或U + 12345678字符(如果允许,则为字符)为blanks。这就是为什么您要使用-D SUPPORT_ROGUE_LOCALES以覆盖这些,尽管这意味着扫描整个列表需要花费更多时间。

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

我无法使用@ChrisDown,因为python没有POSIX字符类的接口。

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


我认为这实际上是唯一的方法,但是它会遇到一些问题,首先是您使用先验知识来决定法律代码点的范围。至少从理论上讲,如果您使用的是Unicode charmap,则字符类与脚本无关(根据Unicode标准,而不是C语言环境),但是Unicode“常规类别”也与C字符类不同。顺便说一句,glibc的i18n ctypes还包括两个字符类:combiningcombining_level3(即iswctype(i, wctype("combining"))
rici 2014年

@rici,请参见编辑(以及该问题)。
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.