Lệnh truy xuất danh sách các ký tự trong một lớp ký tự đã cho trong miền địa phương hiện tại


18

Điều gì có thể là một cách để lấy một danh sách tất cả các nhân vật trong một lớp nhân vật nhất định (như blank, alpha, digit...) trong miền địa phương hiện nay.

Ví dụ,

LC_ALL=en_GB.UTF-8 that-command blank

lý tưởng, trên hệ thống Debian của tôi, sẽ hiển thị một cái gì đó như:

      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

Và trong miền địa phương C có thể hiển thị một cái gì đó như:

09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE

Đó là, biểu diễn của ký tự trong miền địa phương theo các mảng byte, (như UTF-8 trong ví dụ đầu tiên và byte đơn trong giây), mã hóa ký tự Unicode tương đương và mô tả.

Bối cảnh

(chỉnh sửa) Bây giờ lỗ hổng đã được vá và tiết lộ từ lâu, tôi có thể thêm một chút bối cảnh.

Tôi đã hỏi câu hỏi đó tại thời điểm tôi đang điều tra CVE 2014-0485 . glibccó một lỗi ở chỗ nó cho phép người dùng sử dụng các ngôn ngữ như thế LC_ALL=../../../../tmp/evil-localeđược giải quyết liên quan đến đường dẫn tìm kiếm ngôn ngữ hệ thống tiêu chuẩn và do đó cho phép sử dụng bất kỳ tệp nào làm định nghĩa miền địa phương.

Tôi có thể tạo một ngôn ngữ giả mạo chẳng hạn với một ký tự byte cho mỗi ký tự trong đó hầu hết các ký tự ngoại trừ s, hvà một vài ký tự khác được coi là khoảng trống và sẽ bashchạy shtrong khi phân tích /etc/bash.bashrctệp Debian điển hình (và có thể được sử dụng để truy cập shell trên một gitví dụ máy chủ lưu trữ được cung cấp bashđược sử dụng làm vỏ đăng nhập của gitngười dùng máy chủ và sshmáy chủ chấp nhận LC_*/ LANGbiến và kẻ tấn công có thể tải tệp lên máy chủ).

Bây giờ, nếu tôi từng tìm thấy một LC_CTYPE(định nghĩa miền địa phương được biên dịch) /tmp/evil, làm thế nào tôi tìm ra nó là một kẻ bất hảo và theo cách nào.

Vì vậy, mục tiêu của tôi là hủy biên dịch định nghĩa miền địa phương đó, và nếu không, ít nhất là biết ký tự nào (cùng với mã hóa của chúng) nằm trong một lớp ký tự nhất định.

Vì vậy, với ý nghĩ đó:

  • Các giải pháp xem xét các tệp nguồn cho miền địa phương (các định nghĩa miền địa phương giống như các định dạng trong /usr/share/i18n/localeDebian) không được sử dụng trong trường hợp của tôi.
  • Thuộc tính ký tự Unicode là không liên quan. Tôi chỉ quan tâm đến những gì địa phương nói. Trên hệ thống Debian, thậm chí giữa hai vị trí hệ thống UTF-8, hãy để một mình những kẻ lừa đảo, danh sách các ký tự trong một lớp có thể khác nhau.
  • Các công cụ như recode, pythonhoặc perlthực hiện chuyển đổi byte / đa byte sang / từ ký tự không thể được sử dụng vì chúng có thể (và trong thực tế) thực hiện chuyển đổi theo cách khác với ngôn ngữ.

Đối với hầu hết các địa phương, cuối cùng nó đến từ công cụ LC_CTYPE trong (với glibc) /usr/share/i18n/locales/i18n... tất nhiên phần lớn đến từ Cơ sở dữ liệu Ký tự Unicode. Tất nhiên, thật tuyệt khi có một mệnh lệnh
derobert

@derobert, vâng, trong khi locale(ít nhất là GNU) lấy ra nhiều thông tin được lưu trữ trong nhiều danh mục, những thứ không phải là thông tin quan trọng nhất trong LC_CTYPE và LC_COLLATE. Tôi tự hỏi nếu có một API ẩn để lấy thông tin đó hoặc giải nén thông tin ngôn ngữ.
Stéphane Chazelas

Vâng - bạn có thể lấy thông tin đó được phân tích cú pháp - Cuối cùng tôi cũng đã có được để hoàn thành chỉnh sửa của mình. Có một số lệnh bạn có thể đã cài đặt - ít nhất là tôi đã làm và thậm chí tôi không biết về chúng. Tôi hy vọng nó sẽ giúp. Cụ thể recodeuconvcó thể cung cấp cho bạn những gì bạn nói bạn đang tìm kiếm. Thậm chí có thể luitodtôi đoán ...
mikeerv

Điều đó rất tốt! Điều đó có nghĩa là bạn không cần perlgì cả, tôi nghĩ vậy.
mikeerv

Về cơ bản, tôi dường như có thể trích xuất bộ ký tự của mình LC_CTYPEchỉ với od -A n -t c <LC_CTYPE | tsortCó lẽ bạn đã thử nó rồi, nhưng tôi chưa bao giờ nghe về nó trước đây và tôi đã đọc qua infovà nó nhắc nhở tôi về điều này - và nó dường như hoạt động. Cũng có ptxnhưng tôi nghĩ nó ít liên quan hơn. Dù sao, nếu bạn chưa thử và quyết định làm như vậy - cảnh báo công bằng - nó đòi hỏi một chút kiên nhẫn. lehman.cuny.edu/cgi-bin/man-cgi?tsort+1
mikeerv

Câu trả lời:


7

GIẢI PHÁP CUỐI CÙNG

Vì vậy, tôi đã lấy tất cả các thông tin dưới đây và đưa ra điều này:

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

LƯU Ý :

Tôi sử dụng odlàm bộ lọc cuối cùng ở trên để ưu tiên và vì tôi biết tôi sẽ không làm việc với các ký tự nhiều byte, nên nó sẽ không xử lý chính xác. recode u2..dumpcả hai sẽ tạo đầu ra giống như được chỉ định trong câu hỏi và xử lý chính xác các ký tự rộng.

ĐẦU RA

        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 chương trình

Như tôi trình bày dưới đây, recodesẽ cung cấp cho bạn bản đồ nhân vật hoàn chỉnh của bạn. Theo hướng dẫn của nó, nó thực hiện điều này trước tiên theo giá trị hiện tại của DEFAULT_CHARSETbiến môi trường hoặc, nếu không, nó hoạt động chính xác như bạn chỉ định:

Khi một tên bộ ký tự bị bỏ qua hoặc để trống, giá trị của DEFAULT_CHARSETbiến trong môi trường được sử dụng thay thế. Nếu biến này không được xác định, recodethư viện sẽ sử dụng mã hóa của miền địa phương hiện tại. Trên các hệ thống tuân thủ POSIX , điều này phụ thuộc vào giá trị không trống đầu tiên trong số các biến môi trường LC_ALL, LC_CTYPE, LANGvà có thể được xác định thông qua lệnhlocale charmap.

Cũng đáng chú ý recodelà nó là một api :

Chương trình có tên recodechỉ là một ứng dụng của thư viện mã hóa. Thư viện mã hóa có sẵn riêng cho các chương trình C khác. Một cách tốt để có được sự quen thuộc với thư viện mã hóa là làm quen với recodechính chương trình.

Để sử dụng thư viện mã hóa sau khi được cài đặt, chương trình C cần phải có một dòng:

#include <recode.h>

Để so sánh chuỗi thân thiện với quốc tế Các tiêu chuẩn POSIXCđịnh nghĩa strcoll()hàm:

Các strcoll()chức năng có trách nhiệm so sánh chuỗi trỏ đến bởi s1vào chuỗi trỏ đến bởi s2, cả hai hiểu là phù hợp với loại LC_COLLATE của miền địa phương hiện nay.

Các strcoll()chức năng sẽ không thay đổi các thiết lập của errno nếu thành công.

Vì không có giá trị trả về được dành riêng để chỉ ra lỗi, nên một ứng dụng muốn kiểm tra các tình huống lỗi sẽ đặt errno thành 0, sau đó gọi strcoll(), sau đó kiểm tra errno.

Đây là một ví dụ riêng về việc sử dụng nó:

#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);
}

Về các POSIXlớp ký tự, bạn đã lưu ý rằng bạn đã sử dụng CAPI để tìm các lớp này. Đối với các ký tự và lớp unicode, bạn có thể sử dụng recode's bộ ký tự kết xuất có tên để có đầu ra mong muốn. Từ hướng dẫn của nó một lần nữa :

Ví dụ, lệnh recode l2..full < inputngụ ý một chuyển đổi cần thiết từ Latin-2 sang UCS-2,dump-with-name chỉ được kết nối từ UCS-2. Trong các trường hợp như vậy, recodekhông hiển thị mã Latin-2 gốc trong kết xuất, chỉ có các giá trị UCS-2 tương ứng . Để đưa ra một ví dụ đơn giản hơn, lệnh

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

tạo ra đầu ra sau:

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)

Nhận xét mô tả được đưa ra bằng tiếng Anh và ASCII, tuy nhiên nếu mô tả bằng tiếng Anh không có nhưng tiếng Pháp là, thì mô tả tiếng Pháp được đưa ra thay vào đó, sử dụng tiếng Latin-1. Tuy nhiên, nếu biến LANGUAGEhoặc LANGmôi trường bắt đầu bằng các chữ cái fr , thì ưu tiên liệt kê sẽ chuyển sang tiếng Pháp khi cả hai mô tả đều có sẵn.

Sử dụng cú pháp tương tự như trên kết hợp với tập dữ liệu thử nghiệm đi kèm, tôi có thể lấy bản đồ ký tự của riêng mình với:

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

ĐẦU RA

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)

Nhưng đối với các nhân vật thông thường, recoderõ ràng là không cần thiết. Điều này sẽ cung cấp cho bạn các ký tự được đặt tên cho mọi thứ trong bộ ký tự 128 byte:

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

ĐẦU RA

 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

Tất nhiên, chỉ có 128 byte được biểu diễn, nhưng đó là vì ngôn ngữ địa phương của tôi, không bùa chú hay không, sử dụng bảng mã ASCII và không có gì nữa. Vì vậy, đó là tất cả những gì tôi nhận được. Nếu tôi chạy nó mà không luitlọc nó, mặc dù vậy, odnó sẽ cuộn lại và in lại cùng một bản đồ\0400.

Có hai vấn đề lớn với phương pháp trên, mặc dù. Đầu tiên là thứ tự đối chiếu của hệ thống - đối với các địa phương không phải ASCII, các giá trị khớp cho bộ ký tự không chỉ đơn giản là trong sequence, mà theo tôi nghĩ, có khả năng là cốt lõi của vấn đề bạn đang cố gắng giải quyết.

Chà, tr's mantrang GNU tuyên bố rằng nó sẽ mở rộng các [:upper:] [:lower:]lớp theo thứ tự - nhưng đó không phải là nhiều.

Tôi tưởng tượng một số giải pháp nặng tay có thể được thực hiện với sortnhưng đó sẽ là một công cụ khá khó sử dụng cho API lập trình phụ trợ.

recodesẽ làm điều này một cách chính xác, nhưng bạn dường như không quá yêu thích chương trình này vào ngày khác. Có thể các chỉnh sửa ngày nay sẽ đưa ra ánh sáng thân thiện hơn với nó hoặc có thể không.

GNU cũng cung cấp gettextthư viện hàm và dường như có thể giải quyết vấn đề này ít nhất là cho LC_MESSAGESbối cảnh:

- Chức năng: char * bind_textdomain_codeset( const char *domainname, const char *codeset)

Các bind_textdomain_codesetchức năng có thể được sử dụng để xác định các bộ ký tự đầu ra cho catalog nhắn cho miền domainname . Các codeset luận phải là một giá trị codeset tên mà có thể được sử dụng cho các iconv_open chức năng, hoặc một con trỏ null.

Nếu codeset tham số là con trỏ null, bind_textdomain_codeset trả về đang được chọn codeset cho tên miền có tên domainname . Nó trả về NULL nếu không codeset vẫn chưa được chọn.

Các bind_textdomain_codesetchức năng có thể được sử dụng nhiều lần. Nếu được sử dụng nhiều lần với cùng một đối số tên miền, cuộc gọi sau sẽ ghi đè cài đặt được thực hiện bởi cuộc gọi trước đó.

Các bind_textdomain_codesetchức năng trả về một con trỏ đến một chuỗi chứa tên của codeset chọn. Chuỗi được phân bổ nội bộ trong chức năng và người dùng không được thay đổi. Nếu hệ thống đi ra khỏi lõi trong quá trình thực thi bind_textdomain_codeset, giá trị trả về là NULL và biến toàn cục errno được đặt tương ứng.

Bạn cũng có thể sử dụng các loại ký tự Unicode gốc , độc lập với ngôn ngữ và từ bỏ các lớp POSIX hoàn toàn hoặc có thể gọi cho loại trước để cung cấp cho bạn đủ thông tin để xác định loại sau.

Ngoài các biến chứng, Unicode cũng mang đến những khả năng mới. Một là mỗi ký tự Unicode thuộc về một loại nhất định . Bạn có thể ghép một ký tự thuộc danh mục "chữ cái" \p{L}. Bạn có thể ghép một ký tự không thuộc danh mục đó với \P{L}.

Một lần nữa, "ký tự" thực sự có nghĩa là "điểm mã Unicode". \p{L}khớp với một điểm mã duy nhất trong danh mục "chữ cái". Nếu chuỗi đầu vào của bạn được à mã hóa dưới dạng U+0061 U+0300, nó khớp với amà không có dấu. Nếu đầu vào được àmã hóa dưới dạng U+00E0, nó khớp àvới dấu. Lý do là cả hai điểm mã U+0061 (a)U+00E0 (à)nằm trong danh mục "chữ cái", trong khi đó U+0300là trong danh mục "đánh dấu".

Bây giờ bạn nên hiểu tại sao \P{M}\p{M}*+là tương đương với \X. \P{M}khớp với một điểm mã không phải là dấu kết hợp, trong khi \p{M}*+ khớp với 0 hoặc nhiều điểm mã đang kết hợp dấu. Để phù hợp với một chữ cái bao gồm bất kỳ dấu phụ, sử dụng \p{L}\p{M}*+. Regex cuối cùng này sẽ luôn khớp à, bất kể nó được mã hóa như thế nào. Bộ định lượng sở hữu đảm bảo rằng quay lui không gây ra trùng \P{M}\p{M}*+khớp với dấu không có dấu mà không có dấu kết hợp theo sau, điều \X này sẽ không bao giờ làm được.

Trang web tương tự cung cấp thông tin trên cũng thảo luận về việc triển khai biểu thức Tclchính quy POSIX -compliant của chính họ có thể là một cách khác để đạt được mục tiêu của bạn.

Và cuối cùng trong số các giải pháp tôi sẽ đề nghị bạn có thể tự hỏi LC_COLLATEtệp cho bản đồ ký tự hệ thống hoàn chỉnh và theo thứ tự. Điều này có vẻ không dễ thực hiện, nhưng tôi đã đạt được một số thành công với những điều sau đây sau khi biên dịch nó localedefnhư được trình bày dưới đây:

<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

Đó là, thừa nhận, hiện đang thiếu sót nhưng tôi hy vọng nó chứng minh khả năng ít nhất.

TỪ CÁI NHÌN ĐẦU TIÊN

strings $_/en_GB

#OUTPUT

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

Nó thực sự không giống lắm nhưng sau đó tôi bắt đầu nhận thấy copycác lệnh trong danh sách. Ví dụ, tệp ở trên dường như copytrong "en_US" , và một tệp lớn thực sự khác mà dường như tất cả chúng đều chia sẻ ở một mức độ nào đó iso_14651_t1_common.

Nó khá lớn:

strings $_ | wc -c

#OUTPUT
431545

Đây là phần giới thiệu /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>;

...

Bạn có thể grepthông qua điều này tất nhiên, nhưng bạn có thể chỉ:

recode -lf gb

Thay thế. Bạn sẽ nhận được một cái gì đó như thế này:

Dec  Oct Hex   UCS2  Mne  BS_4730

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

... VÀ HƠN THẾ NỮA

Ngoài ra còn có luitthiết bị dịch UTF-8 đầu cuối, ptytôi đoán rằng nó hoạt động giữa các Xterm mà không có hỗ trợ UTF-8. Nó xử lý rất nhiều công tắc - chẳng hạn như ghi nhật ký tất cả các byte được chuyển đổi thành một tệp hoặc -cdưới dạng một |pipebộ lọc đơn giản .

Tôi chưa bao giờ nhận ra có quá nhiều thứ này - bản đồ địa phương và nhân vật và tất cả những thứ đó. Đây rõ ràng là một vấn đề rất lớn nhưng tôi đoán tất cả diễn ra sau hậu trường. Có - ít nhất là trên hệ thống của tôi - vài trăm man 3kết quả liên quan cho các tìm kiếm liên quan đến ngôn ngữ.

Và cũng có:

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

Điều đó sẽ diễn ra trong một thời gian rất dài.

Các Xlibchức năng xử lý việc này mọi lúc - luitlà một phần của gói đó.

Các Tcl_uni...chức năng có thể chứng minh là hữu ích là tốt.

chỉ cần <tab>hoàn thành một chút và mantìm kiếm và tôi đã học được khá nhiều về chủ đề này.

Với localedef- bạn có thể biên dịch localestrong I18Nthư mục của bạn . Đầu ra rất thú vị và không hữu ích lắm - không giống như charmapstất cả - nhưng bạn có thể nhận được định dạng thô giống như bạn chỉ định ở trên như tôi đã làm:

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

Sau đó, với odbạn có thể đọc nó - byte và chuỗi:

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

Mặc dù đó là một chặng đường dài để giành chiến thắng trong một cuộc thi sắc đẹp, đó là đầu ra có thể sử dụng. Và odlà cấu hình như bạn muốn nó là tất nhiên.

Tôi đoán tôi cũng đã quên về những điều này:

    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

Tôi có lẽ đã quên chúng vì tôi không thể khiến chúng làm việc. Tôi không bao giờ sử dụng Perlvà tôi không biết làm thế nào để tải một mô-đun đúng cách tôi đoán. Nhưng các mantrang trông khá đẹp. Trong mọi trường hợp, một cái gì đó cho tôi biết bạn sẽ thấy việc gọi mô-đun Perl ít nhất là khó khăn hơn một chút so với tôi. Và, một lần nữa, những thứ này đã có trên máy tính của tôi - và tôi thậm chí không bao giờ sử dụng Perl. Cũng có một số đáng chú ý I18Nmà tôi đã cuộn một cách thận trọng bằng cách biết rõ tôi cũng sẽ không làm cho họ làm việc.


1
Đó là tất cả thông tin rất hay và hữu ích, nhưng nó cung cấp thông tin về các tệp nguồn (trong i18n) có thể hoặc không được sử dụng để tạo miền địa phương tôi hiện đang sử dụng. Thông tin ngôn ngữ có thể đến từ /usr/lib/locale/locale-archivehoặc /some/dir/LC_CTYPE, và đó là phần có liên quan đến ngôn ngữ của tôi được lưu trữ trong các tệp mà tôi theo dõi.
Stéphane Chazelas

@StephaneChezales - vì vậy chỉ cần trích xuất LC_STUFFtừ kho lưu trữ với localedef- nó cũng làm điều đó. Tôi có thể demo nó là tốt, tôi đoán. Bạn cũng có thể xem đó và khá nhiều mọi thứ khác với stringshoặc odhoặc bất kỳ phần còn lại. Tôi đã làm, dù sao. Nhưng bằng cách này - sự charmaps các bạn đang locale hiện đang sử dụng - và localedefsẽ báo cáo về điều đó. Ngoài ra đó là những gì recodequá.
mikeerv

Về cơ bản, bạn đang nói rằng chúng ta có thể làm bằng tay những thư viện của hệ thống để thực hiện thông tin lớp ký tự truy vấn, nhưng điều đó sẽ cần hàng ngàn dòng mã để làm điều đó một cách đáng tin cậy và kết quả sẽ cụ thể theo hệ thống. (phân tích môi trường giống như thư viện hệ thống thực hiện (LOCPATH, LANG, LANGUAGE, LC_CTYPE ..., xác định nơi tìm dữ liệu, giải nén nó ...). Tôi không thể xem cách trích xuất nội dung từ kho lưu trữ với localedef mặc dù.
Stéphane Chazelas

@StephaneChazelas - Tôi không đề nghị bạn làm điều đó bằng tay - Tôi đề nghị bạn làm điều đó với một máy tính - sử dụng mã nhị phân hệ thống như od, recode, uconvvà phần còn lại. Nhưng đó là sai lầm của tôi - nó không phải localedeflà trích xuất nó, nó recodesẽ như vậy. Bạn đã phải kiểm tra info recode- và bên cạnh recodelệnh bảng tôi hiển thị có nhiều thứ tương tự - và tôi sẽ xử lý mọi thứ theo cách tương tự, tôi nghĩ vậy. Nó không chỉ kéo bộ ký tự của bạn ra khỏi không khí mỏng. Trong mọi trường hợp tôi đã có hy vọng cao cho các perlmô-đun đó - bạn đã thử chưa?
mikeerv

1
Nếu có một API để truy xuất danh sách các ký tự trong một lớp ký tự đã cho trong miền địa phương hiện tại, thì đó chính là thứ tôi đang tìm kiếm. Nếu bạn có thể chứng minh làm thế nào để làm điều này, tôi sẽ chấp nhận câu trả lời. Điều duy nhất tôi có thể nghĩ đến (và làm thế nào tôi có được "đầu ra mong đợi" trong câu hỏi của mình) là sử dụng iswblank(3)cho tất cả các giá trị ký tự có thể.
Stéphane Chazelas

1

Trên các hệ thống GNU, FreeBSD hoặc Solaris, phương pháp vũ phu này hoạt động:

#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;
}

Mặc dù theo C / POSIX, wchar_tlà loại mờ không liên quan đến Unicode và chỉ được đảm bảo bao gồm tất cả các ký tự được hỗ trợ bởi ngôn ngữ của hệ thống, trong thực tế, trong hầu hết các hệ thống hỗ trợ Unicode, các giá trị tương ứng với các điểm mã Unicode và các định nghĩa miền địa phương dựa trên Unicode.

Unicode có nghĩa là một siêu ký tự của tất cả các bộ ký tự đã biết, do đó, việc lặp qua tất cả các điểm mã hợp lệ trong Unicode (0 đến 0xD7FF và 0xE000 đến 0x10FFFF) nên liệt kê ít nhất tất cả các ký tự được hỗ trợ bởi một bộ ký tự cụ thể.

Ở đây, chúng tôi đang sử dụng API tiêu chuẩn của miền địa phương để kiểm tra xem API nào thuộc loại đã cho và để chuyển đổi nó thành dạng được mã hóa của chúng trong mã hóa của miền địa phương. Chúng tôi chỉ sử dụng perlcharnamesmô-đun của nó để lấy tên từ một bảng mã Unicode đã cho.

Trên các địa điểm sử dụng mã hóa trạng thái như ISO-2022-JP, chúng tôi đảm bảo biểu mẫu được mã hóa được hiển thị từ trạng thái ban đầu mặc định.

Tôi đã không tìm thấy một hệ thống đã cài đặt các ngôn ngữ có mã hóa ký tự trạng thái nhưng ít nhất là trên các hệ thống GNU, có thể tạo ra một số ngôn ngữ giả mạo có thể được tạo ra (và ít nhất các công cụ GNU không hoạt động chính xác trong đó địa phương). Chẳng hạn, với một miền địa phương tùy chỉnh sử dụng ISO-2022-JP với ja_JPmiền địa phương bình thường , tôi nhận được:

$ 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

So sánh với:

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

Trong ISO-2022-JP, 1B 24 42chuỗi ( \e$B) chuyển từ ASCII sang trạng thái trong đó các ký tự được thể hiện dưới dạng 2 byte (7 bit) (ở đây 21 21 cho KHÔNG GIAN IDEOGRAPHIC). Trong khi ở EUCJP, đó là cùng một byte nhưng việc chuyển đổi trạng thái được thực hiện bằng cách lật bit thứ 8 ( A1 = 21 | 0x80) làm cho nó không trạng thái hơn.

Điều đó có nghĩa là trong các mã hóa trạng thái đó, có một số cách để viết một ký tự đã cho (ví dụ bằng cách chèn một vài chuỗi chuyển đổi trạng thái đó ) và chuỗi được hiển thị bởi mã đó ở trên chỉ là một trong số chúng (cách chính tắc từ chữ cái đầu tiên Tình trạng mặc định).

Trong khi đối với một miền địa phương bình thường, các ký tự không thể nằm ngoài 0..0xD7FF, 0xE000..0x10FFFF, đối với miền địa phương giả mạo , bất kỳ ký tự nào trong phạm vi được hỗ trợ bởi wchar_t đều có thể. Chẳng hạn, tôi có thể tạo một miền địa phương trong đó các ký tự U + DCBA hoặc U + 12345678 (hoặc sẽ là ký tự nếu chúng được cho phép) là khoảng trống . Đó là lý do tại sao bạn muốn biên dịch mã đó -D SUPPORT_ROGUE_LOCALESđể bao gồm các mã đó, mặc dù điều đó có nghĩa là phải mất nhiều thời gian hơn để quét toàn bộ danh sách.

Tôi không thể sử dụng giải pháp của @ mikeerv vì recodesử dụng các chuyển đổi của chính nó, không còn được duy trì và chỉ hỗ trợ các ký tự Unicode tối đa 0xFFFF và trít nhất GNU không hoạt động với các ký tự nhiều byte.

Tôi không thể sử dụng @ ChrisDownpythonkhông có giao diện cho các lớp ký tự POSIX.

Tôi đã thử Perl, nhưng nó không có thật cho các điểm mã trong khoảng từ 128 đến 255 cho các địa điểm nhiều byte khác với UTF-8 và không sử dụng các thư viện chuyển đổi của hệ thống.


Tôi nghĩ rằng đây thực sự là cách duy nhất để làm điều đó, nhưng nó gặp phải một số vấn đề, bắt đầu với thực tế là bạn đã sử dụng kiến ​​thức trước đó để quyết định về phạm vi của các mật mã pháp lý. Về lý thuyết, ít nhất, nếu bạn đang sử dụng bùa chú Unicode, các lớp ký tự độc lập với tập lệnh (theo tiêu chuẩn Unicode, không phải là ngôn ngữ C), nhưng "danh mục chung" Unicode cũng không giống với các lớp ký tự C. BTW, ctypes i18n glibc bao gồm thêm hai lớp nhân vật: combiningcombining_level3(tức. iswctype(i, wctype("combining")))
rici

@rici, xem chỉnh sửa (và cũng của câu hỏi).
Stéphane Chazelas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.