Команда для получения списка символов в данном классе символов в текущей локали

18

Что может быть способ , чтобы получить список всех символов в заданном классе символов (как 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-0475 . glibcбыла ошибка в том, что он позволял пользователю использовать такие локали LC_ALL=../../../../tmp/evil-locale, которые разрешены относительно стандартного системного пути поиска локали и, таким образом, позволял использовать любой файл в качестве определения локали.

Я мог бы создать мошенническую локаль, например, с одним байтом на символьную кодировку, где большинство символов, за исключением s, hи некоторые другие считались пробелами, и это bashвыполнялось бы shпри синтаксическом анализе типичного /etc/bash.bashrcфайла Debian (и это можно было бы использовать для получения доступа к оболочке в gitНапример, предоставленный хост-сервер bashиспользуется как оболочка для входа gitпользователя сервера, и что sshсервер принимает LC_*/ LANGпеременные и что злоумышленник может загружать файлы на сервер).

Теперь, если я когда-нибудь найду LC_CTYPE(скомпилированное определение локали) /tmp/evil, как я узнаю, что это было мошенническое и каким образом.

Поэтому моя цель состоит в том, чтобы откомпилировать эти определения локали и, если нет, хотя бы узнать, какие символы (вместе с их кодировкой) находятся в данном классе символов.

Итак, имея в виду:

  • Решения, которые ищут исходные файлы для локали (определения локали, такие как в /usr/share/i18n/localeDebian), в моем случае бесполезны.
  • Свойства символов Юникода не имеют значения. Я забочусь только о том, что говорит язык. В системе Debian, даже между двумя локалями системы UTF-8, не говоря уже о мошеннических, список символов в классе может отличаться.
  • Такие инструменты , как recode, pythonили perlчто делать байт / мульти-байт / с преобразования символов не может быть использован , как они могут (и на практике делают) сделать преобразование иначе , чем локали.
Стефан Шазелас
источник
Для большинства локалей это в конечном итоге происходит из материала LC_CTYPE (с glibc) /usr/share/i18n/locales/i18n... который, конечно же, в основном из базы данных символов Unicode. Конечно, было бы неплохо иметь команду
Дероберт
@derobert, да, хотя locale(по крайней мере, GNU) извлекает много информации, хранящейся во многих категориях, вещи, которые не являются наиболее важными в LC_CTYPE и LC_COLLATE. Интересно, есть ли скрытый API для извлечения этой информации или компиляции информации о локали.
Стефан Шазелас
Да - вы можете разобрать эту информацию - я наконец-то нашел время закончить редактирование. Есть несколько команд, которые вы, вероятно, уже установили - по крайней мере, я сделал, и я даже не знал о них. Я надеюсь, что это помогает. В частности, recodeи uconvможет дать вам то, что вы говорите, что вы ищете. Возможно, даже просто, luitи odя думаю ...
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

Ответы:

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является просто приложением своей библиотеки перекодирования. Библиотека перекодировки доступна отдельно для других программ на Си. Хороший способ познакомиться с библиотекой перекодирования - познакомиться с самой recodeпрограммой.

Чтобы использовать библиотеку перекодирования после ее установки, программе на Си должна быть строка:

#include <recode.h>

Для международно безвредного сравнения струны POSIXи Cстандарты определяют strcoll()функцию:

strcoll()Функция должна сравнивать строку , на которую указывает s1на строку , на которую указывает s2, как интерпретируются в зависимости от обстоятельств к LC_COLLATE категории текущей локали.

strcoll()Функция не изменяет настройки егто в случае успеха.

Поскольку никакое возвращаемое значение не зарезервировано для указания ошибки, приложение, желающее проверить наличие ошибок, должно установить 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, чтобы найти их. Для символов и классов Юникода вы можете использовать кодировку recode's dump-with-names для получения желаемого результата. Из руководства снова :

Например, команда 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-байтовой кодировке:

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влияют, что, как я думаю, вероятно, является ядром проблемы, которую вы пытаетесь решить.

Ну, на tr's manстранице GNU говорится, что она будет расширять [:upper:] [:lower:]классы по порядку, но это немного.

Я полагаю, что можно было бы реализовать какое-то сложное решение, sortно это было бы довольно громоздким инструментом для API программирования бэкэнда.

recodeбудет делать это правильно, но вы, кажется, не слишком влюбились в программу на днях. Может быть, сегодняшние изменения проливают свет на это, а могут и нет.

GNU также предлагает gettextбиблиотеку функций, и кажется, что она способна решить эту проблему, по крайней мере, для LC_MESSAGESконтекста:

- Функция: char * bind_textdomain_codeset( const char *domainname, const char *codeset)

bind_textdomain_codesetФункция может быть использована для определения набора символов для вывода каталогов сообщений для домена имя_домена . Codeset аргумент должен быть допустимым Codeset именем , которое может быть использовано для 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

По общему признанию, в настоящее время она несовершенна, но я надеюсь, что она хотя бы продемонстрирует такую ​​возможность.

НА ПЕРВОМ СИНЕСТИ

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терминальное ptyустройство перевода UTF-8, которое, по-моему, служит посредником для XTerms без поддержки UTF-8. Он обрабатывает много переключателей - таких как запись всех преобразованных байтов в файл или -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- вы можете скомпилировать 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

Затем 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которые я задумчиво прокручивал, прекрасно зная, что я не заставлю их работать.

mikeserv
источник
1
Это все очень хорошая и полезная информация, но она дает информацию об исходных файлах (в i18n), которые могли или не могли быть использованы для создания локали, которую я сейчас использую. Информация о локали, вероятно, поступает из /usr/lib/locale/locale-archiveили /some/dir/LC_CTYPE, и это часть, относящаяся к моей локали, которая хранится в тех файлах, которые мне нужны.
Стефан Шазелас
@StephaneChezales - так что просто извлеките ваш файл LC_STUFFиз архива localedef- он тоже это сделает. Думаю, я тоже могу это продемонстрировать. Вы также можете просмотреть это и почти все остальное с stringsили с odлюбым другим . Во всяком случае, я сделал. Но, кстати charmaps , языковой стандарт, которым вы сейчас пользуетесь, localedefтакже сообщит об этом. И вот что recodeделает тоже.
mikeserv
По сути, вы говорите, что мы можем вручную делать то, что системные библиотеки запрашивают информацию о классах символов, но для этого потребуется тысячи строк кода, и результат будет зависеть от системы. (анализирует среду так же, как это делает системная библиотека (LOCPATH, LANG, LANGUAGE, LC_CTYPE ..., определяет, где искать данные, извлекает их ...). Я не вижу, как извлечь материал из архива хотя с localedef
Стефан Шазелас
@StephaneChazelas - Я не предлагаю вам сделать это вручную - я предлагаю вам сделать это с помощью компьютера - с помощью системных исполняемых файлов , таких как od, recode, uconvи все остальное. Но это была моя ошибка - дело не в localedefэтом, а в recodeтом , что так и будет. Вы должны проверить info recode- и помимо команды recodeтаблицы, которую я показываю, есть почти то же самое - и она будет обрабатывать вещи таким же образом, я думаю. Он не просто вытаскивает вашу кодировку из воздуха. В любом случае, я возлагал большие надежды на эти perlмодули - вы пробовали их?
mikeserv
1
Если есть API для получения списка символов в данном классе символов в текущей локали, то это именно то, что я ищу. Если вы сможете продемонстрировать, как это сделать, я приму ответ. Единственное, о чем я мог подумать (и как я получил «ожидаемый результат» в моем вопросе), это использовать iswblank(3)для всех возможных значений символов.
Стефан Шазелас
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 42sequence ( \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использует его собственные преобразования, больше не поддерживается и поддерживает только символы Unicode до 0xFFFF, а GNU trпо крайней мере не работает с многобайтовыми символами.

Я не мог использовать @ ChrisDown's, так как pythonне имеет интерфейсов с классами символов POSIX.

Я пробовал Perl, но он является поддельным для кодовых точек между 128 и 255 для многобайтовых локалей, отличных от UTF-8, и не использует системные библиотеки преобразования.

Стефан Шазелас
источник
Я думаю, что это фактически единственный способ сделать это, но он страдает от нескольких проблем, начиная с того, что вы использовали предварительные знания, чтобы принять решение о диапазоне правовых кодовых точек. В теории, по крайней мере, если вы используете кодировку Unicode, классы символов не зависят от сценария (согласно стандарту Unicode, а не языковым стандартам C), но «общие категории» Unicode также не совпадают с классами символов C. Кстати, типы glibc i18n включают в себя еще два класса символов: combiningи combining_level3(то есть iswctype(i, wctype("combining")))
Ричи,
@rici, см. правку (а также вопрос).
Стефан Шазелас