POSSÍVEL SOLUÇÃO FINAL
Então, eu peguei todas as informações abaixo e criei 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
NOTA :
Eu uso od
como o filtro final acima como preferência e porque sei que não vou trabalhar com caracteres de vários bytes, com os quais ele não lidará corretamente. recode u2..dump
ambos irão gerar uma saída mais semelhante à especificada na pergunta e manipularão caracteres largos corretamente.
RESULTADO
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
você fornecerá seu mapa de caracteres completo. De acordo com o manual, ele faz isso de acordo com o valor atual da DEFAULT_CHARSET
variável de ambiente ou, na sua falta, opera exatamente como você especifica:
Quando um nome de conjunto de caracteres é omitido ou deixado em branco, o valor da DEFAULT_CHARSET
variável no ambiente é usado. Se essa variável não estiver definida, a recode
biblioteca usará a codificação do código do idioma atual. Em sistemas compatíveis com POSIX , isso depende do primeiro valor não vazio entre as variáveis de ambiente LC_ALL, LC_CTYPE, LANG
e pode ser determinado através do comandolocale charmap.
Também digno de nota recode
é que é uma API :
O programa chamado recode
é apenas uma aplicação de sua biblioteca de recodificação. A biblioteca de recodificação está disponível separadamente para outros programas em C. Uma boa maneira de adquirir alguma familiaridade com a biblioteca de recodificação é familiarizando-se com o recode
próprio programa.
Para usar a biblioteca de recodificação após a instalação, um programa C precisa ter uma linha:
#include <recode.h>
Para comparação internacionalmente amigável de strings Os padrões POSIX
e C
definem a strcoll()
função:
A strcoll()
função deve comparar a string apontada por s1
com a string apontada por s2
, ambas interpretadas conforme apropriado para a categoria LC_COLLATE da localidade 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 deseje verificar se há situações de erro deve definir errno como 0, depois ligar
strcoll()
e verificar errno.
Aqui está um exemplo localizado separadamente 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);
}
Com relação às POSIX
classes de caracteres, você já observou que usou a C
API para encontrá-las. Para caracteres e classes unicode, você pode usar charset recode's
dump-with-names para obter a saída desejada. De seu manual novamente :
Por exemplo, o comando recode l2..full < input
implica uma conversão necessária do Latin-2 para o UCS-2, pois o dump-with-names é conectado apenas a partir do UCS-2. Nesses casos, recode
não exibe os códigos Latin-2 originais
no dump, apenas os valores UCS-2 correspondentes . 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; no entanto, se a descrição em inglês não estiver disponível, mas houver uma em francês, a descrição em francês será fornecida usando o Latin-1. No entanto, se a
variável de ambiente LANGUAGE
ou LANG
começar com as letras fr , a preferência de listagem será em francês quando as duas descrições estiverem disponíveis.
Usando uma sintaxe semelhante à acima combinada com seu conjunto de dados de teste incluído, posso obter meu próprio mapa de caracteres com:
recode -q u8/test8..dump </dev/null
RESULTADO
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 caracteres comuns, recode
aparentemente não é necessário. Isso deve fornecer chars nomeados para tudo no conjunto de 128 bytes:
printf %b "$(printf \\%04o $(seq 128))" |
luit -c |
od -A n -t o1z -t a -w12
RESULTADO
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
Obviamente, apenas 128 bytes são representados, mas isso ocorre porque meu local, utf-8 charmaps ou não, usa o conjunto de caracteres ASCII e nada mais. Então é tudo que eu recebo. Se eu o executasse sem luit
filtrá-lo, od
o reverteria e imprimiria o mesmo mapa novamente até\0400.
Existem dois grandes problemas com o método acima. Primeiro, há a ordem de intercalação do sistema - para localidades não ASCII, os valores de mordida para os conjuntos de caracteres não são simplesmente in seq
uêncios, o que, como eu acho, é provavelmente o núcleo do problema que você está tentando resolver.
Bem, a tr's man
página do GNU afirma que expandirá as [:upper:]
[:lower:]
classes em ordem - mas isso não é muito.
Eu imagino que alguma solução pesada possa ser implementada, sort
mas isso seria uma ferramenta bastante difícil 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 tenham uma luz mais amigável ou talvez não.
O GNU também oferece a gettext
biblioteca de funções e parece capaz de resolver esse problema pelo menos no 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 o domínio
domainname . O argumento do conjunto de códigos deve ser um nome de conjunto de códigos válido que possa ser usado para a função iconv_open ou um ponteiro nulo.
Se o parâmetro codeset for o ponteiro nulo, bind_textdomain_codeset
retornará o conjunto de códigos atualmente selecionado para o domínio com o nome
domainname . Retorna NULL se nenhum conjunto de códigos ainda foi selecionado.
A bind_textdomain_codeset
função pode ser usada várias vezes. Se usada várias vezes com o mesmo argumento domainname, a chamada posterior substitui as configurações feitas pela anterior.
A bind_textdomain_codeset
função retorna um ponteiro para uma seqüência de caracteres que contém o nome do conjunto de códigos selecionado. A sequência é alocada internamente na função e não deve ser alterada pelo usuário. Se o sistema ficou fora do núcleo durante a execução de
bind_textdomain_codeset
, o valor de retorno é NULL e a variável global errno é configurada de acordo.
Você também pode usar categorias de caracteres Unicode nativas , que são independentes do idioma e renunciam completamente às classes POSIX, ou talvez chamar o primeiro para fornecer informações suficientes para definir o último.
Além de complicações, o Unicode também traz novas possibilidades. Uma é que cada caractere Unicode pertence a uma determinada categoria. Você pode combinar um único caractere pertencente à categoria "letra" com
\p{L}
. Você pode combinar um único caractere não pertencente a essa categoria \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 a sua sequência de entrada estiver à
codificada como U+0061 U+0300
, ela corresponderá a
sem o acento. Se a entrada estiver à
codificada como U+00E0
, ela combina à
com o acento. O motivo é que o código aponta U+0061 (a)
e U+00E0 (à)
está na categoria "letra", enquanto U+0300
está na categoria "marca".
Agora você deve entender por que \P{M}\p{M}*+
o equivalente a \X
.
\P{M}
corresponde a um ponto de código que não é uma marca combinada, enquanto \p{M}*+
corresponde a zero ou mais pontos de código que estão combinando marcas. Para corresponder a uma letra incluindo quaisquer sinais diacríticos, use \p{L}\p{M}*+
. Esse último regex sempre corresponderá à
, independentemente de como é codificado. O quantificador possessivo garante que o retorno não \P{M}\p{M}*+
corresponda a uma não marca sem as marcas combinadas a seguir, o que \X
nunca faria.
O mesmo site que forneceu as informações acima também discute Tcl
a implementação de regex compatível com POSIX, que pode ser outra maneira de atingir seu objetivo.
E, por último, entre as soluções, sugerirei que você possa interrogar o LC_COLLATE
arquivo em si para obter o mapa de caracteres completo e em ordem do sistema. Isso pode não parecer fácil, mas obtive algum sucesso com o seguinte, depois de compilá-lo localedef
como 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 demonstre a possibilidade pelo menos.
NO PRIMEIRO BLUSH
strings $_/en_GB
#OUTPUT
int_select "<U0030><U0030>"
...
END LC_TELEPHONE
Realmente não parecia muito, mas então comecei a perceber copy
comandos em toda a lista. O arquivo acima parece copy
em "en_US", por exemplo, e outro realmente grande 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 fazer grep
isso, é claro, mas você pode apenas:
recode -lf gb
Em vez de. Você obteria algo como isto:
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
Também há luit
um pty
dispositivo de tradução UTF-8 terminal, acho que funciona 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 -c
como um |pipe
filtro simples .
Eu nunca percebi que havia tanto nisso - os locais e os mapas de personagens e tudo isso. Aparentemente, isso é um grande negócio, mas acho que tudo acontece nos bastidores. Existem - pelo menos no meu sistema - algumas centenas de man 3
resultados relacionados para pesquisas relacionadas ao código do idioma.
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 continuará por muito tempo.
As Xlib
funções lidam com isso o tempo todo - luit
faz parte desse pacote.
As Tcl_uni...
funções também podem ser úteis.
apenas um pouco de <tab>
conclusão e man
pesquisas e aprendi bastante sobre esse assunto.
Com localedef
- você pode compilar locales
no seu I18N
diretório. A saída é descolada e não é extraordinariamente útil - não é como a de charmaps
todo -, 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 ler - 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 ganhar um concurso de beleza, essa é uma saída utilizável. E od
é tão configurável quanto você deseja que seja, é claro.
Acho que também esqueci disso:
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 me 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 bem legais. De qualquer forma, algo me diz que você encontrará 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 o Perl. Também há alguns I18N
que eu percorri melancolicamente sabendo muito bem que também não os faria funcionar.
/usr/share/i18n/locales/i18n
... o qual, é claro, vem em grande parte do banco de dados de caracteres Unicode. Claro, seria bom ter um comando