Какой может быть способ получить список всех символов в заданном классе символов (например 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/locale
Debian), в моем случае бесполезны. - Свойства символов Unicode не имеют значения. Меня волнует только то, что говорит локаль. В системе Debian, даже между двумя системными локалями UTF-8, не говоря уже о мошеннических, список символов в классе может быть разным.
- Такие инструменты, как
recode
,python
или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
, или, если это не так, онработает именно так, как вы указываете:
Когданабор символовИмя пропущено или оставлено пустым, вместо него используется значение переменной
DEFAULT_CHARSET
в среде. Если эта переменная не определена, библиотекаrecode
используеткодировка текущей локали.НаPOSIXВ совместимых системах это зависит от первого непустого значения среди переменных средыLC_ALL, LC_CTYPE, LANG
и может быть определено с помощью командыlocale charmap.
Также стоит отметить recode
, что этоэто API:
Названная программа
recode
— это просто приложение ее библиотеки перекодирования. Библиотека перекодирования доступна отдельно для других программ на языке C. Хороший способ познакомиться с библиотекой перекодирования — это ознакомиться сrecode
самой программой.Чтобы использовать библиотеку перекодирования после ее установки, программа на языке C должна иметь строку:
#include <recode.h>
Для международного сравнения строк POSIX
стандарты C
определяют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
классов символов, вы уже отметили, что использовали C
API для их поиска. Для символов и классов Unicode вы можете использоватьrecode's
дамп-с-именамиcharset для получения желаемого вывода. Из егоснова руководство:
Например, команда
recode l2..full < input
подразумевает необходимое преобразование изЛатинский-2кУКС-2,какдамп-с-именамиподключен только изУКС-2.В таких случаяхrecode
не отображает оригинал Латинский-2коды в дампе, только соответствующиеУКС-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. Однако если переменная окружения
LANGUAGE
илиLANG
начинается с буквфр, то предпочтение отдается французскому языку, если доступны оба описания.
Используя аналогичный синтаксис, приведенный выше, в сочетании с еговключен тестовый набор данныхЯ могу получить свою собственную карту персонажей с помощью:
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карты или нет, используетASCIIcharset и ничего больше. Так что это все, что я получаю. Если бы я запустил его без luit
фильтрации, od
то откатил бы его назад и снова напечатал бы ту же карту до\0400.
Однако с вышеприведенным методом связаны две основные проблемы. Во-первых, это порядок сортировки системы — для локалей, отличных от ASCII, значения байтов для наборов символов не просто влияют seq
, что, как я думаю, скорее всего, является ядром проблемы, которую вы пытаетесь решить.
Ну, на странице GNU tr's man
указано, что классы будут расширяться [:upper:]
[:lower:]
по порядку, но это не так уж много.
Я полагаю, что можно реализовать какое-то более сложное решение, sort
но это будет довольно громоздкий инструмент для API бэкэнд-программирования.
recode
сделает это правильно, но вы, кажется, не слишком любили программу на днях. Может быть, сегодняшние правки прольют на нее более дружелюбный свет, а может и нет.
GNU также предлагает gettext
библиотеку функций, и, кажется, она способнарешить эту проблемупо крайней мере для LC_MESSAGES
контекста:
— Функция:
char * bind_textdomain_codeset
(const char *domainname, const char *codeset
)Функцию
bind_textdomain_codeset
можно использовать для указания выходного набора символов для каталогов сообщений для домена. доменное имя.набор кодоваргумент должен быть действительнымнабор кодовимя, которое может быть использовано длязначокv_openфункция или нулевой указатель.Еслинабор кодовпараметр — нулевой указатель,
bind_textdomain_codeset
возвращает текущий выбранныйнабор кодовдля домена с именем доменное имя. Возвращает NULL, если нетнабор кодовпока не выбран.Функцию
bind_textdomain_codeset
можно использовать несколько раз. Если использовать ее несколько раз с одним и тем же аргументом domainname, то более поздний вызов переопределяет настройки, сделанные предыдущим.Функция
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
в"ru_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
которое, как я полагаю, действует как посредник для 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
которые я с тоской прокрутил, прекрасно понимая, что я тоже не заставлю их работать.
решение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, может быть. Например, я мог бы создать locale, где символы U+DCBA или U+12345678 (или были бы символами, если бы они были разрешены) являютсяпробелы. Вот почему вам нужно скомпилировать этот код, чтобы -D SUPPORT_ROGUE_LOCALES
охватить их, хотя это означает, что потребуется гораздо больше времени для сканирования всего списка.
Я не смог воспользоваться решением @mikeserv, поскольку recode
оно использует собственные преобразования, больше не поддерживается и поддерживает только символы Unicode до 0xFFFF, а GNU, tr
по крайней мере, не работает с многобайтовыми символами.
Я не мог использовать@ChrisDown'sне python
имеет интерфейсов к классам символов POSIX.
Я попробовал Perl, но он не работает с кодовыми точками от 128 до 255 для многобайтовых локалей, отличных от UTF-8, и не использует системные библиотеки преобразования.