POSIBLE SOLUCIÓN FINAL
Así que tomé toda la información a continuación y llegué a esto:
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 :
Lo uso od
como el filtro final anterior por preferencia y porque sé que no trabajaré con caracteres de varios bytes, que no manejará correctamente. recode u2..dump
ambos generarán una salida más parecida a la especificada en la pregunta y manejarán los caracteres anchos correctamente.
SALIDA
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 PROGRAMADOR
Como lo demuestro a continuación, recode
le proporcionaré su mapa completo de personajes. De acuerdo con su manual, lo hace de acuerdo con el valor actual de la DEFAULT_CHARSET
variable de entorno o, en su defecto, funciona exactamente como usted especifica:
Cuando se omite un nombre de conjunto de caracteres o se deja vacío, DEFAULT_CHARSET
se utiliza el valor de la variable en el entorno. Si esta variable no está definida, la recode
biblioteca usa la codificación del entorno local actual. En los sistemas compatibles con POSIX , esto depende del primer valor no vacío entre las variables de entorno LC_ALL, LC_CTYPE, LANG
y se puede determinar mediante el comandolocale charmap.
También vale la pena señalar recode
que es una API :
El programa nombrado recode
es solo una aplicación de su biblioteca de recodificación. La biblioteca de recodificación está disponible por separado para otros programas en C. Una buena forma de familiarizarse con la biblioteca de recodificación es familiarizarse con el recode
programa en sí.
Para usar la biblioteca de recodificación una vez que está instalada, un programa en C necesita tener una línea:
#include <recode.h>
Para una comparación de cadenas internacionalmente amigable Los estándares POSIX
y C
definen la strcoll()
función:
La strcoll()
función comparará la cadena apuntada por s1
con la cadena apuntada por s2
, ambas interpretadas como apropiadas para la categoría LC_COLLATE de la localización actual.
La strcoll()
función no cambiará la configuración de errno si tiene éxito.
Como no se reserva ningún valor de retorno para indicar un error, una aplicación que desee verificar si hay situaciones de error debe establecer errno en 0, luego llamar
strcoll()
y luego verificar errno.
Aquí hay un ejemplo de su uso ubicado por separado :
#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);
}
Con respecto a las POSIX
clases de caracteres, ya has notado que usaste la C
API para encontrarlos. Para caracteres y clases unicode, puede usar el juego de caracteres recode's
dump-with-names para obtener el resultado deseado. De su manual nuevamente :
Por ejemplo, el comando recode l2..full < input
implica una conversión necesaria de Latin-2 a UCS-2, ya que dump-with-names solo está conectado desde UCS-2. En tales casos, recode
no muestra los códigos originales de
Latin-2 en el volcado, solo los valores UCS-2 correspondientes . Para dar un ejemplo más simple, el comando
echo 'Hello, world!' | recode us..dump
produce el siguiente resultado:
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)
El comentario descriptivo se da en inglés y en ASCII, pero si la descripción en inglés no está disponible pero sí en francés, entonces la descripción en francés se da en su lugar, usando Latin-1. Sin embargo, si la
variable de entorno LANGUAGE
o LANG
comienza con las letras fr , entonces la preferencia de listado va al francés cuando ambas descripciones están disponibles.
Utilizando una sintaxis similar a la anterior combinada con su conjunto de datos de prueba incluido , puedo obtener mi propio mapa de caracteres con:
recode -q u8/test8..dump </dev/null
SALIDA
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)
Pero para los personajes comunes, recode
aparentemente no es necesario. Esto debería darle caracteres con nombre para todo en el conjunto de caracteres de 128 bytes:
printf %b "$(printf \\%04o $(seq 128))" |
luit -c |
od -A n -t o1z -t a -w12
SALIDA
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
Por supuesto, solo se representan 128 bytes, pero eso se debe a que mi configuración regional, utf-8 charmaps o no, usa el juego de caracteres ASCII y nada más. Entonces eso es todo lo que obtengo. Sin luit
embargo, si lo ejecutara sin filtrarlo, od
lo rodaría e imprimiría el mismo mapa nuevamente hasta\0400.
Sin embargo, hay dos problemas principales con el método anterior. Primero está el orden de clasificación del sistema: para los entornos locales que no son ASCII, los valores de mordida de los conjuntos de caracteres no son simplemente in seq
uence, lo que, según creo, es probablemente el núcleo del problema que está tratando de resolver.
Bueno, la tr's man
página de GNU afirma que expandirá las [:upper:]
[:lower:]
clases en orden, pero eso no es mucho.
Me imagino que podría implementarse una solución de mano dura, sort
pero sería una herramienta bastante difícil de manejar para una API de programación de back-end.
recode
hará esto correctamente, pero el otro día no parecías demasiado enamorado del programa. Tal vez las ediciones de hoy arrojen una luz más amigable o tal vez no.
GNU también ofrece la gettext
biblioteca de funciones, y parece ser capaz de abordar este problema al menos para el LC_MESSAGES
contexto:
- Función: char * bind_textdomain_codeset
( const char *domainname,
const char *codeset
)
La bind_textdomain_codeset
función se puede utilizar para especificar el juego de caracteres de salida para los catálogos de mensajes para el dominio nombre de dominio
. El argumento del conjunto de códigos debe ser un nombre de conjunto de códigos válido que pueda usarse para la función iconv_open o un puntero nulo.
Si el parámetro del conjunto de códigos es el puntero nulo, bind_textdomain_codeset
devuelve el conjunto de códigos seleccionado actualmente para el dominio con el nombre
nombrededominio . Devuelve NULL si todavía no se ha seleccionado un conjunto de códigos .
La bind_textdomain_codeset
función se puede usar varias veces. Si se usa varias veces con el mismo argumento de nombre de dominio, la llamada posterior anula la configuración realizada por la anterior.
La bind_textdomain_codeset
función devuelve un puntero a una cadena que contiene el nombre del conjunto de códigos seleccionado. La cadena se asigna internamente en la función y el usuario no debe cambiarla. Si el sistema se salió del núcleo durante la ejecución de
bind_textdomain_codeset
, el valor de retorno es NULL y la variable global errno se establece en consecuencia.
También puede usar categorías de caracteres Unicode nativas , que son independientes del idioma y renuncian a las clases POSIX por completo, o tal vez recurrir a las primeras para proporcionarle suficiente información para definir las últimas.
Además de las complicaciones, Unicode también trae nuevas posibilidades. Una es que cada personaje Unicode pertenece a una determinada categoría. Puede hacer coincidir un solo carácter perteneciente a la categoría "letra" con
\p{L}
. Puede hacer coincidir un solo personaje que no pertenece a esa categoría con \P{L}
.
Nuevamente, "carácter" realmente significa "punto de código Unicode". \p{L}
coincide con un único punto de código en la categoría "letra". Si su cadena de entrada está à
codificada como U+0061 U+0300
, coincide a
sin el acento. Si la entrada está à
codificada como U+00E0
, coincide à
con el acento. La razón es que tanto el código señala U+0061 (a)
como U+00E0 (à)
está en la categoría "letra", mientras que U+0300
está en la categoría "marca".
Ahora debe entender por qué \P{M}\p{M}*+
es el equivalente de \X
.
\P{M}
coincide con un punto de código que no es una marca de combinación, mientras que \p{M}*+
coincide con cero o más puntos de código que son marcas de combinación. Para hacer coincidir una letra con cualquier signo diacrítico, use \p{L}\p{M}*+
. Esta última expresión regular siempre coincidirá à
, independientemente de cómo esté codificada. El cuantificador posesivo se asegura de que el retroceso no haga \P{M}\p{M}*+
coincidir una no marca sin las marcas combinadas que la siguen, lo cual \X
nunca funcionaría.
El mismo sitio web que proporcionó la información anterior también analiza Tcl
la propia implementación de expresiones regulares que cumple con POSIX, que podría ser otra forma de lograr su objetivo.
Y por último, entre las soluciones, sugeriré que puede interrogar el LC_COLLATE
archivo en sí para obtener el mapa de caracteres del sistema completo y en orden . Puede que esto no parezca fácil, pero logré algo de éxito con lo siguiente después de compilarlo localedef
como se muestra a continuación:
<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
Es, sin duda, actualmente defectuoso, pero espero que al menos demuestre la posibilidad.
AL PRIMER RUBOR
strings $_/en_GB
#OUTPUT
int_select "<U0030><U0030>"
...
END LC_TELEPHONE
Realmente no parecía mucho, pero luego comencé a notar copy
comandos en toda la lista. El archivo anterior parece estar copy
en "en_US", por ejemplo, y otro muy grande que parece que todos comparten hasta cierto punto es iso_14651_t1_common
.
Es bastante grande:
strings $_ | wc -c
#OUTPUT
431545
Aquí está la introducción a /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>;
...
Puede hacerlo, grep
por supuesto, pero podría simplemente:
recode -lf gb
En lugar. Obtendrías algo como esto:
Dec Oct Hex UCS2 Mne BS_4730
0 000 00 0000 NU null (nul)
1 001 01 0001 SH start of heading (soh)
...
... Y MÁS
También hay luit
un pty
dispositivo de traducción UTF-8 terminal , supongo que actúa como intermediario para XTerms sin soporte UTF-8. Maneja muchos modificadores, como registrar todos los bytes convertidos en un archivo o -c
como un |pipe
filtro simple .
Nunca me di cuenta de que había tanto en esto: las configuraciones regionales y los mapas de caracteres y todo eso. Aparentemente esto es un gran problema, pero supongo que todo sucede detrás de escena. Hay, al menos en mi sistema, un par de cientos de man 3
resultados relacionados para búsquedas relacionadas con la configuración regional.
Y también hay:
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
...
Eso va a continuar por un muy largo tiempo.
Las Xlib
funciones manejan esto todo el tiempo, luit
es parte de ese paquete.
Las Tcl_uni...
funciones también pueden resultar útiles.
solo un poco de <tab>
finalización y man
búsquedas, y he aprendido bastante sobre este tema.
Con localedef
: puede compilarlo locales
en su I18N
directorio. El resultado es funky y no extraordinariamente útil, no como charmaps
en absoluto, pero puede obtener el formato sin procesar tal como lo especificó anteriormente como lo hice yo:
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
Luego con od
usted puede leerlo: bytes y cadenas:
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...<
...
Aunque está muy lejos de ganar un concurso de belleza, esa es una salida utilizable. Y od
es tan configurable como quieras que sea, por supuesto.
Supongo que también me olvidé de estos:
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
Probablemente me olvidé de ellos porque no pude hacer que trabajaran. Supongo que nunca uso Perl
y no sé cómo cargar un módulo correctamente. Pero las man
páginas se ven muy bien. En cualquier caso, algo me dice que encontrarás que llamar a un módulo de Perl es al menos un poco menos difícil que yo. Y, nuevamente, estos ya estaban en mi computadora, y ni siquiera uso Perl. También hay notablemente algunos I18N
que me desplacé con melancolía sabiendo muy bien que tampoco los haría funcionar.
/usr/share/i18n/locales/i18n
... que, por supuesto, proviene en gran medida de la base de datos de caracteres Unicode. Por supuesto, sería bueno tener un comando