用於檢索目前語言環境中給定字元類別中的字元清單的命令

用於檢索目前語言環境中給定字元類別中的字元清單的命令

有什麼方法可以檢索當前語言環境中給定字元類別(例如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 模組至少比我稍微容易一些。還有一些值得注意的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 之間的程式碼點來說是假的,並且不使用系統的轉換庫。

相關內容