POSSIBILE SOLUZIONE FINALE
Quindi ho preso tutte le informazioni di seguito e ho escogitato questo:
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 :
Uso od
come filtro finale sopra per preferenza e perché so che non lavorerò con caratteri multi-byte, che non gestirà correttamente. recode u2..dump
genererà entrambi un output più simile a quello specificato nella domanda e gestirà correttamente i caratteri di grandi dimensioni.
PRODUZIONE
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 DEL PROGRAMMATORE
Come dimostrerò di seguito, recode
ti fornirà la tua mappa completa dei personaggi. Secondo il suo manuale, lo fa in base al valore corrente della DEFAULT_CHARSET
variabile d'ambiente o, in mancanza, funziona esattamente come specificato:
Quando un nome di set di caratteri viene omesso o lasciato vuoto, DEFAULT_CHARSET
viene invece utilizzato il valore della variabile nell'ambiente. Se questa variabile non è definita, la recode
libreria utilizza la codifica della locale corrente. Sui sistemi compatibili con POSIX , questo dipende dal primo valore non vuoto tra le variabili di ambiente LC_ALL, LC_CTYPE, LANG
e può essere determinato tramite il comandolocale charmap.
Vale anche la pena notare recode
che è un api :
Il programma denominato recode
è solo un'applicazione della sua libreria di ricodifica. La libreria di ricodifica è disponibile separatamente per altri programmi C. Un buon modo per acquisire familiarità con la libreria di ricodifica è familiarizzare con il recode
programma stesso.
Per utilizzare la libreria di ricodifica una volta installata, un programma C deve avere una riga:
#include <recode.h>
Per un confronto di stringhe a livello internazionale Gli standard POSIX
e C
definiscono la strcoll()
funzione:
La strcoll()
funzione deve confrontare la stringa puntata da s1
con la stringa puntata da s2
, entrambe interpretate come appropriate alla categoria LC_COLLATE della locale corrente.
La strcoll()
funzione non deve modificare l'impostazione di errno in caso di successo.
Poiché nessun valore di ritorno è riservato per indicare un errore, un'applicazione che desidera verificare la presenza di situazioni di errore dovrebbe impostare errno su 0, quindi chiamare
strcoll()
, quindi controllare errno.
Ecco un esempio separato del suo utilizzo:
#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);
}
Per quanto riguarda le POSIX
classi di personaggi, hai già notato che hai utilizzato l' C
API per trovarle. Per caratteri e classi unicode puoi usare recode's
dump-with-names set di per ottenere l'output desiderato. Dal suo manuale di nuovo :
Ad esempio, il comando recode l2..full < input
implica una conversione necessaria da Latin-2 a UCS-2, poiché il dump-with-names è collegato solo da UCS-2. In tali casi, recode
non visualizza i codici Latin-2 originali
nel dump, ma solo i corrispondenti valori UCS-2 . Per dare un esempio più semplice, il comando
echo 'Hello, world!' | recode us..dump
produce il seguente output:
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)
Il commento descrittivo è dato in inglese e ASCII, ma se la descrizione inglese non è disponibile ma è francese, allora viene data la descrizione francese usando Latin-1. Tuttavia, se la
variabile di ambiente LANGUAGE
o LANG
inizia con le lettere fr , l'elenco delle preferenze passa al francese quando sono disponibili entrambe le descrizioni.
Utilizzando una sintassi simile a quella sopra combinata con il set di dati di test incluso, posso ottenere la mia mappa dei caratteri con:
recode -q u8/test8..dump </dev/null
PRODUZIONE
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)
Ma per i personaggi comuni, recode
apparentemente non è necessario. Questo dovrebbe darti caratteri nominati per tutto nel set di caratteri a 128 byte:
printf %b "$(printf \\%04o $(seq 128))" |
luit -c |
od -A n -t o1z -t a -w12
PRODUZIONE
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
Naturalmente, sono rappresentati solo 128 byte, ma questo perché il mio locale, charmaps utf-8 o no, usa l' ASCII set di e nient'altro. Quindi è tutto ciò che ottengo. Se lo avessi eseguito senza luit
filtrarlo, l' od
avrei riavvolto e avrei stampato di nuovo la stessa mappa\0400.
Tuttavia, ci sono due problemi principali con il metodo sopra. Per prima cosa c'è l'ordine di confronto del sistema: per i locali non ASCII i valori del morso per i set di caratteri non sono semplicemente inseq
uence, il che, come penso, è probabilmente il nocciolo del problema che stai cercando di risolvere.
Bene, la tr's man
pagina GNU afferma che espanderà il[:upper:]
[:lower:]
classi in ordine - ma non è molto.
Immagino che potrebbe essere implementata una soluzione pesante sort
ma che sarebbe uno strumento piuttosto ingombrante per un'API di programmazione back-end.
recode
farà questa cosa correttamente, ma l'altro giorno non sei sembrato troppo innamorato del programma. Forse le modifiche di oggi getteranno una luce più amichevole o forse no.
GNU offre anche la gettext
libreria di funzioni e sembra essere in grado di risolvere questo problema almeno perLC_MESSAGES
contesto:
- Funzione: char * bind_textdomain_codeset
( const char *domainname,
const char *codeset
)
La bind_textdomain_codeset
funzione può essere utilizzata per specificare il set di caratteri di output per i cataloghi di messaggi per il dominio nome dominio
. L' argomento del set di codici deve essere un nome di set di codici valido che può essere utilizzato per codici funzione iconv_open o un puntatore null.
Se il parametro codeset è il puntatore null, bind_textdomain_codeset
restituisce il set di codici attualmente selezionato per il dominio con il nome
domainname . Restituisce NULL se non è stato ancora selezionato alcun set di codici .
La bind_textdomain_codeset
funzione può essere utilizzata più volte. Se utilizzata più volte con lo stesso argomento domainname, la chiamata successiva sovrascrive le impostazioni effettuate dalla precedente.
La bind_textdomain_codeset
funzione restituisce un puntatore a una stringa contenente il nome del set di codici selezionato. La stringa è allocata internamente nella funzione e non deve essere modificata dall'utente. Se il sistema è uscito dal core durante l'esecuzione di
bind_textdomain_codeset
, il valore restituito è NULL e la variabile globale errno è impostata di conseguenza.
È inoltre possibile utilizzare categorie di caratteri Unicode nativi , che sono indipendenti dalla lingua e rinunciare del tutto alle classi POSIX, o forse chiamare il primo per fornire informazioni sufficienti per definire il secondo.
Oltre alle complicazioni, Unicode offre anche nuove possibilità. Uno è che ogni personaggio Unicode appartiene a una determinata categoria. Puoi abbinare un singolo personaggio appartenente alla categoria "lettera" con
\p{L}
. Puoi abbinare un singolo personaggio non appartenente a quella categoria con\P{L}
.
Ancora una volta, "carattere" significa in realtà "punto di codice Unicode". \p{L}
corrisponde a un singolo punto di codice nella categoria "lettera". Se la stringa di input è à
codificata come U+0061 U+0300
, corrisponde a
senza l'accento. Se l'ingresso è à
codificato come U+00E0
, corrisponde à
all'accento. Il motivo è che entrambi i punti del codice U+0061 (a)
e U+00E0 (à)
sono nella categoria "lettera", mentreU+0300
è nella categoria "segno".
Ora dovresti capire perché \P{M}\p{M}*+
è l'equivalente di \X
.
\P{M}
corrisponde a un punto di codice che non è un segno combinato, mentre \p{M}*+
corrisponde a zero o più punti di codice che combinano segni. Per abbinare una lettera compresi eventuali segni diacritici, utilizzare \p{L}\p{M}*+
. Quest'ultima regex corrisponderà sempre à
, indipendentemente da come viene codificata. Il quantificatore possessivo si assicura che il backtracking non causi \P{M}\p{M}*+
l'abbinamento di un non-mark senza i segni combinati che lo seguono, cosa \X
che non farebbe mai.
Lo stesso sito web che ha fornito le informazioni di cui sopra anche discute Tcl
proprio l' POSIX -compatibile implementazione regex che potrebbe essere un altro modo per raggiungere il tuo obiettivo.
E infine, tra le soluzioni suggerirò che è possibile interrogare il LC_COLLATE
file stesso per la mappa completa e in ordine dei caratteri del sistema. Questo può non sembrare facile, ma dopo averlo compilato localedef
come dimostrato di seguito ho ottenuto un certo successo con quanto segue:
<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
È vero, attualmente, è imperfetto, ma spero che dimostri almeno la possibilità.
A PRIMA VISTA
strings $_/en_GB
#OUTPUT
int_select "<U0030><U0030>"
...
END LC_TELEPHONE
In realtà non sembrava molto, ma poi ho iniziato a notare i copy
comandi in tutto l'elenco. Il file sopra sembra copy
in "en_US" per esempio, e un altro vero e proprio grande che sembra che tutti condividano in una certa misura è iso_14651_t1_common
.
È abbastanza grande:
strings $_ | wc -c
#OUTPUT
431545
Ecco l'intro per /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>;
...
Puoi grep
farlo ovviamente, ma potresti semplicemente:
recode -lf gb
Anziché. Otterresti qualcosa del genere:
Dec Oct Hex UCS2 Mne BS_4730
0 000 00 0000 NU null (nul)
1 001 01 0001 SH start of heading (soh)
...
... E ALTRO
Esiste anche luit
un pty
dispositivo di traduzione terminale UTF-8 che suppone che agisca da intermediario per XTerms senza il supporto UTF-8. Gestisce molti switch, come la registrazione di tutti i byte convertiti in un file o -c
come semplice|pipe
filtro.
Non mi ero mai reso conto che ci fosse così tanto in questo - i locali, le mappe dei personaggi e tutto il resto. Apparentemente questo è un grosso problema, ma immagino che succeda tutto dietro le quinte. Ce ne sono - almeno sul mio sistema - duecentoman 3
risultati correlati per le ricerche relative alle impostazioni locali.
E c'è anche:
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
...
Andrà avanti per molto tempo.
Le Xlib
funzioni gestiscono tutto il tempo -luit
fa parte di quel pacchetto.
Il Tcl_uni...
funzioni potrebbero rivelarsi utili.
solo un piccolo <tab>
completamento e man
ricerche e ho imparato molto su questo argomento.
Con localedef
- puoi compilare il locales
nella tua I18N
directory. L'output è funky e non straordinariamente utile - non come charmaps
affatto - ma puoi ottenere il formato raw proprio come hai specificato sopra come ho fatto io:
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
Quindi con od
te puoi leggerlo - byte e stringhe:
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...<
...
Anche se è molto lontano dal vincere un concorso di bellezza, questo è un output utilizzabile. Eod
è configurabile come vuoi che sia, ovviamente.
Immagino di aver dimenticato anche questi:
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
Probabilmente me ne sono dimenticato perché non riuscivo a farli funzionare. Non uso mai Perl
e non so come caricare correttamente un modulo, immagino. Ma le man
pagine sembrano piuttosto carine. In ogni caso, qualcosa mi dice che troverai che chiamare un modulo Perl almeno un po 'meno difficile di me. E, ancora una volta, questi erano già sul mio computer - e non uso nemmeno Perl. Ci sono anche alcuni I18N
che ho girato malinconicamente sapendo benissimo che non li avrei nemmeno fatti funzionare.
/usr/share/i18n/locales/i18n
... che ovviamente proviene in gran parte dal database dei caratteri Unicode. Certo, sarebbe bello avere un comando