Qual poderia ser uma maneira de recuperar uma lista de todos os caracteres em uma determinada classe de caracteres (como ,,, blank
... ) na localidade atual.alpha
digit
Por exemplo,
LC_ALL=en_GB.UTF-8 that-command blank
idealmente, no meu sistema Debian, exibiria algo como:
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
E na localidade C poderia exibir algo como:
09 U+0009 HORIZONTAL TAB
20 U+0020 SPACE
Ou seja, a representação do caractere no local em termos de matrizes de bytes (como UTF-8 no primeiro exemplo e byte único no segundo), o ponto de código do caractere Unicode equivalente e uma descrição.
Contexto
(editar) Agora que a vulnerabilidade foi corrigida e divulgada há muito tempo, posso adicionar um pouco de contexto.
Eu fiz essa pergunta no momento em que estava investigandoCVE 2014-0475. glibc
tinha um bug que permitia ao usuário usar localidades como LC_ALL=../../../../tmp/evil-locale
aquelas resolvidas em relação ao caminho de pesquisa de localidade padrão do sistema e, portanto, permitir o uso de qualquer arquivo como definição de localidade.
Eu poderia criar um local não autorizadopor exemplo, com um único byte por conjunto de caracteres de caracteres, onde a maioria dos caracteres s
, exceto h
e alguns outros, foram consideradosespaços em brancoe isso seria bash
executado sh
ao analisar um arquivo Debian típico /etc/bash.bashrc
(e que poderia ser usado para obter acesso shell em um git
servidor de hospedagem, por exemplo, desde que bash
seja usado como o shell de login do git
usuário do servidor e que o ssh
servidor aceite LC_*
/ LANG
variáveis e que o invasor possa fazer upload de arquivos para o servidor).
Agora, se eu encontrasse uma LC_CTYPE
(definição de localidade compilada) em /tmp/evil
, como descobriria que era uma definição desonesta e de que maneira.
Portanto, meu objetivo é descompilar essas definições de localidade e, se não, pelo menos saber quais caracteres (junto com sua codificação) estão em uma determinada classe de caracteres.
Então, com isso em mente:
- Soluções que analisam os arquivos de origem do código do idioma (as definições de código do idioma como as do
/usr/share/i18n/locale
Debian) são inúteis no meu caso. - As propriedades dos caracteres Unicode são irrelevantes. Eu só me importo com o que o local diz. Em um sistema Debian, mesmo entre duas localidades de sistema UTF-8, e muito menos aquelas não autorizadas, a lista de caracteres em uma classe pode ser diferente.
- Ferramentas como
recode
,python
ouperl
que fazem a conversão de bytes/multibytes de/para caracteres não podem ser usadas, pois podem (e na prática fazem) fazer a conversão de uma maneira diferente da localidade.
Responder1
POSSÍVEL SOLUÇÃO FINAL
Então, peguei todas as informações abaixo e cheguei a isso:
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
OBSERVAÇÃO:
Eu uso od
como filtro final acima por preferência e porque sei que não trabalharei com caracteres multibyte, que não serão manipulados corretamente. recode u2..dump
ambos gerarão uma saída mais parecida com a especificada na pergunta e manipularão caracteres largos corretamente.
SAÍDA
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 DO PROGRAMADOR
Como demonstro abaixo, recode
fornecerei a você seu mapa de personagens completo. De acordo com seu manual, ele faz isso primeiro de acordo com o valor atual da DEFAULT_CHARSET
variável de ambiente ou, na sua falta,funciona exatamente como você especifica:
Quando umconjunto de caracteresname for omitido ou deixado em branco, o valor da
DEFAULT_CHARSET
variável no ambiente será usado. Se esta variável não estiver definida, arecode
biblioteca usa ocodificação da localidade atual.SobrePOSIXsistemas compatíveis, isso depende do primeiro valor não vazio entre as variáveis de ambienteLC_ALL, LC_CTYPE, LANG
e pode ser determinado através do comandolocale charmap.
Também digno de nota recode
é queé uma API:
O programa nomeado
recode
é apenas uma aplicação de sua biblioteca de recodificação. A biblioteca de recodificação está disponível separadamente para outros programas C. Uma boa maneira de adquirir alguma familiaridade com a biblioteca de recodificação é familiarizar-se com orecode
próprio programa.Para usar a biblioteca de recodificação depois de instalada, um programa C precisa ter uma linha:
#include <recode.h>
Para comparação de strings internacionalmente amigável Os padrões POSIX
e C
definem ostrcoll()
função:
A
strcoll()
função deve comparar a string apontada pors1
com a string apontada pors2
, ambas interpretadas como apropriadas para a categoria LC_COLLATE do código do idioma atual.A
strcoll()
função não deve alterar a configuração de errno se for bem-sucedida.Como nenhum valor de retorno é reservado para indicar um erro, um aplicativo que deseja verificar situações de erro deve definir errno como 0, chamar
strcoll()
e verificar errno.
Aqui está umlocalizado separadamenteexemplo de seu uso:
#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);
}
Em relação às POSIX
classes de personagens, você já notou que usou a C
API para encontrá-las. Para caracteres e classes Unicode você pode usarrecode's
despejo com nomescharset para obter a saída desejada. Do seumanual novamente:
Por exemplo, o comando
recode l2..full < input
implica uma conversão necessária deLatim-2paraUCS-2,comodespejo com nomessó está conectado fora deUCS-2.Nesses casos,recode
não exibe o original Latim-2códigos no dump, apenas os correspondentesUCS-2valores. Para dar um exemplo mais simples, o comando
echo 'Hello, world!' | recode us..dump
produz a seguinte saída:
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)
O comentário descritivo é fornecido em inglês e ASCII, mas se a descrição em inglês não estiver disponível, mas uma em francês estiver, então a descrição em francês será fornecida, usando Latin-1. No entanto, se a variável de ambiente
LANGUAGE
ouLANG
começar com as letrasfranco, a preferência de listagem será em francês quando ambas as descrições estiverem disponíveis.
Usando sintaxe semelhante à acima combinada com seuconjunto de dados de teste incluídoPosso obter meu próprio mapa de personagens com:
recode -q u8/test8..dump </dev/null
SAÍDA
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)
Mas para personagens comuns, recode
aparentemente não é necessário. Isso deve fornecer caracteres nomeados para tudo no conjunto de caracteres de 128 bytes:
printf %b "$(printf \\%04o $(seq 128))" |
luit -c |
od -A n -t o1z -t a -w12
SAÍDA
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
Claro, apenas 128 bytes são representados, mas isso ocorre porque minha localidade,utf-8charmaps ou não, usa oASCIIconjunto de caracteres e nada mais. Então isso é tudo que recebo. Se eu o executasse sem luit
filtrá-lo, od
ele voltaria e imprimiria o mesmo mapa novamente até\0400.
No entanto, existem dois problemas principais com o método acima. Primeiro, há a ordem de agrupamento do sistema - para localidades não-ASCII, os valores de bits dos conjuntos de caracteres não são simplesmente influenciados seq
, o que, na minha opinião, é provavelmente o cerne do problema que você está tentando resolver.
Bem, tr's man
a página GNU afirma que irá expandir as [:upper:]
[:lower:]
classes em ordem - mas isso não é muito.
Imagino que alguma solução pesada poderia ser implementada, sort
mas seria uma ferramenta bastante complicada para uma API de programação de back-end.
recode
fará isso corretamente, mas você não parecia muito apaixonado pelo programa outro dia. Talvez as edições de hoje lancem uma luz mais amigável sobre isso ou talvez não.
GNU também oferece a gettext
biblioteca de funções e parece ser capaz deresolver esse problemapelo menos para o LC_MESSAGES
contexto:
— Função:
char * bind_textdomain_codeset
(const char *domainname, const char *codeset
)A
bind_textdomain_codeset
função pode ser usada para especificar o conjunto de caracteres de saída para catálogos de mensagens para domínio nome do domínio. Oconjunto de códigosargumento deve ser válidoconjunto de códigosnome que pode ser usado paraiconv_openfunção ou um ponteiro nulo.Se oconjunto de códigosparâmetro é o ponteiro nulo,
bind_textdomain_codeset
retorna o atualmente selecionadoconjunto de códigospara o domínio com o nome nome do domínio. Ele retorna NULL se nãoconjunto de códigosainda foi selecionado.A
bind_textdomain_codeset
função pode ser usada diversas vezes. Se usada várias vezes com o mesmo argumento domainname, a chamada posterior substituirá as configurações feitas pela anterior.A
bind_textdomain_codeset
função retorna um ponteiro para uma string contendo o nome do conjunto de códigos selecionado. A string é alocada internamente na função e não deve ser alterada pelo usuário. Se o sistema saiu do núcleo durante a execução debind_textdomain_codeset
, o valor de retorno será NULL e a variável global errno será definida de acordo.
Você também pode usar nativoCaractere Unicodecategorias, que são independentes da linguagem e renunciam completamente às classes POSIX, ou talvez recorrer às primeiras para fornecer informações suficientes para definir as últimas.
Além das complicações, o Unicode também traz novas possibilidades. Uma é que cada caractere Unicode pertence a um determinadocategoria.Você pode combinar um único caractere pertencente à categoria "letra" com
\p{L}
. Você pode combinar um único caractere que não pertença a essa categoria com\P{L}
.Novamente, "caractere" realmente significa "ponto de código Unicode".
\p{L}
corresponde a um único ponto de código na categoria "letra". Se sua string de entrada estiverà
codificada comoU+0061 U+0300
, ela corresponderáa
sem acento. Se a entrada forà
codificada comoU+00E0
, ela corresponderáà
ao acento. O motivo é que tanto os pontos de códigoU+0061 (a)
quantoU+00E0 (à)
os estão na categoria “letra”, enquantoU+0300
estão na categoria “marca”.Agora você deve entender por que
\P{M}\p{M}*+
é equivalente a\X
.\P{M}
corresponde a um ponto de código que não é uma marca de combinação, enquanto\p{M}*+
corresponde a zero ou mais pontos de código que são marcas de combinação. Para corresponder uma letra incluindo quaisquer diacríticos, use\p{L}\p{M}*+
. Esta última regex sempre corresponderá aà
, independente de como ela esteja codificada. O quantificador possessivo garante que o retrocesso não faça\P{M}\p{M}*+
corresponder uma não-marca sem as marcas de combinação que o seguem, o que\X
nunca aconteceria.
O mesmo site que forneceu as informações acima também discuteTcl
próprioPOSIX-compatívelimplementação de regex, que pode ser outra maneira de atingir seu objetivo.
E por último entre as soluções, sugerirei que você possa interrogar o LC_COLLATE
próprio arquivo para obter informações completas eem ordemmapa de caracteres do sistema. Isso pode não parecer fácil, mas obtive algum sucesso com o seguinte depois de compilá-lo conforme localedef
demonstrado abaixo:
<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
É, reconhecidamente, atualmente falho, mas espero que pelo menos demonstre a possibilidade.
À PRIMEIRA COR
strings $_/en_GB
#OUTPUT
int_select "<U0030><U0030>"
...
END LC_TELEPHONE
Realmente não parecia muito, mas comecei a notar copy
comandos em toda a lista. O arquivo acima parece estar copy
em"en_US"por exemplo, e outro grande problema que parece que todos compartilham até certo ponto é iso_14651_t1_common
.
É bem grande:
strings $_ | wc -c
#OUTPUT
431545
Aqui está a introdução para /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>;
...
Você pode grep
fazer isso, é claro, mas pode apenas:
recode -lf gb
Em vez de. Você obteria algo assim:
Dec Oct Hex UCS2 Mne BS_4730
0 000 00 0000 NU null (nul)
1 001 01 0001 SH start of heading (soh)
...
... E MAIS
Há também um dispositivo de tradução luit
terminal UTF-8, pty
que acho que atua como intermediário para XTerms sem suporte a UTF-8. Ele lida com muitas opções - como registrar todos os bytes convertidos em um arquivo ou como um filtro -c
simples .|pipe
Eu nunca percebi que havia tanta coisa nisso – os locais e mapas de personagens e tudo mais. Isto é aparentemente muitoproblemamas acho que tudo acontece nos bastidores. Existem - pelo menos no meu sistema - algumas centenas man 3
de resultados relacionados para pesquisas relacionadas ao local.
E também há:
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
...
Isso vai durar ummuitolongo tempo.
As Xlib
funções cuidam disso o tempo todo - luit
faz parte desse pacote.
As Tcl_uni...
funções também podem ser úteis.
apenas um pouco <tab>
de conclusão e man
pesquisas e aprendi bastante sobre esse assunto.
Com localedef
- você pode compilar locales
em seu I18N
diretório. A saída é descolada e não extraordinariamente útil - nada parecida com a charmaps
- mas você pode obter o formato bruto exatamente como especificou acima, como eu fiz:
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
Então od
você pode lê-lo - bytes e strings:
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...<
...
Embora esteja muito longe de vencer um concurso de beleza, esse é um resultado utilizável. E od
é tão configurável quanto você deseja, é claro.
Acho que também esqueci destes:
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
Provavelmente esqueci deles porque não consegui fazê-los funcionar. Eu nunca uso Perl
e não sei como carregar um módulo corretamente, eu acho. Mas as man
páginas parecem muito boas. De qualquer forma, algo me diz que você achará que chamar um módulo Perl é pelo menos um pouco menos difícil do que eu. E, novamente, eles já estavam no meu computador - e eu nunca uso Perl. Há também alguns I18N
que eu rolei melancolicamente, sabendo muito bem que também não os faria funcionar.
Responder2
Pelo menos em sistemas GNU, FreeBSD ou Solaris, esta abordagem de força bruta funciona:
#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;
}
Embora por C/POSIX wchar_t
seja um tipo opaco que não tem relação com Unicode e só tem garantia de cobrir todos os caracteres suportados pela localidade do sistema, na prática, na maioria dos sistemas que suportam Unicode, os valores correspondem aos pontos de código Unicode e as definições de localidade são baseadas em Unicode.
Unicode deve ser um superconjunto de todos os conjuntos de caracteres conhecidos, portanto, fazer um loop em todos os pontos de código válidos em Unicode (0 a 0xD7FF e 0xE000 a 0x10FFFF) deve listar pelo menos todos os caracteres suportados por um determinado conjunto de caracteres.
Aqui, estamos usando a API padrão de localidade do sistema para verificar quais são de um determinado tipo e convertê-los para sua forma codificada na codificação da localidade. Usamos perl
e seu charnames
módulo apenas para obter o nome de um determinado codepoint Unicode.
Em localidades que usam codificações com estado como ISO-2022-JP, garantimos que o formulário codificado seja exibido a partir de um estado inicial padrão.
Eu não encontrei um sistema que tenha instalado localidades com uma codificação de caracteres com estado, mas pelo menos em sistemas GNU, é possível gerar algumas para que uma localidade não autorizada possa ser criada (e pelo menos as ferramentas GNU não funcionam corretamente nesses localidades). Por exemplo, com uma localidade personalizada que usa ISO-2022-JP com uma ja_JP
localidade normal, recebo:
$ 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
Compare com:
$ LC_ALL=ja_JP.eucjp ~/list-type blank
09 U+0009 CHARACTER TABULATION
20 U+0020 SPACE
A1 A1 U+3000 IDEOGRAPHIC SPACE
Na ISO-2022-JP, a 1B 24 42
sequência ( \e$B
) muda de ASCII para um estado onde os caracteres são expressos como 2 bytes (7 bits) (aqui 21 21 para aquele ESPAÇO IDEOGRÁFICO). Enquanto no EUCJP, são os mesmos bytes, mas omudança de estadoé feito invertendo o 8º bit ( A1 = 21 | 0x80
), o que o torna mais sem estado.
Isso significa que nessas codificações com estado, existem várias maneiras de escrever um determinado caractere (por exemplo, inserindo vários dessesmudança de estadosequências), e a sequência mostrada pelo código acima é apenas uma delas (a canônica de um estado padrão inicial).
Embora para uma localidade normal, os caracteres não podem estar fora de 0..0xD7FF, 0xE000..0x10FFFF, para umpor conta próprialocalidade, qualquer caractere no intervalo suportado por wchar_t pode ser. Por exemplo, eu poderia criar um local onde os caracteres U+DCBA ou U+12345678 (ou seriam caracteres se fossem permitidos) sãoespaços em branco. É por isso que você deseja compilar esse código para -D SUPPORT_ROGUE_LOCALES
cobri-los, embora isso signifique que leva muito mais tempo para verificar a lista inteira.
Não pude usar a solução do @mikeserv porque recode
usa suas próprias conversões, não é mais mantida e só suporta caracteres Unicode até 0xFFFF, e o GNU tr
pelo menos não funciona com caracteres multibyte.
eu não pude usar@ChrisDownas python
não possui interfaces para as classes de caracteres POSIX.
Eu tentei Perl, mas é falso para pontos de código entre 128 e 255 para localidades de vários bytes diferentes de UTF-8 e não usa as bibliotecas de conversão do sistema.