perl - ¿Por qué los caracteres de palabra(/ w) no coinciden con el uso del pragma de configuración regional?
sorting unicode (1)
Por favor, no use el pragma de use locale
quebrado.
Por favor, utilice Unicode::Collate::Locale
para la intercalación de locale. Utiliza las reglas de CLDR, y es completamente portátil y no depende de las configuraciones locales POSIX rotas, que simplemente no funcionan bien.
Si ordena por punto de código, no tiene sentido, pero si ordena utilizando un objeto Unicode::Collate::Locale
construido con la configuración regional de Estonia, obtendrá algo razonable:
Codepoint sort: äðõöüŋšžц
Estonian sort: ðŋšžõäöüц
Además, cuando haces este tipo de punto de código sin formato, te afectan terriblemente las cuestiones de normalización. Considerar:
NFC/NFD sort by codepoint is DIFFERENT
NFC Codepoint sort: äðõöüŋšžц
NFD Codepoint sort: äõöšüžðŋц
NFC/NFD sort in estonian is SAME
NFC Estonian sort: ðŋšžõäöüц
NFD Estonian sort: ðŋšžõäöüц
Y aquí está el programa de demostración que produjo todo eso.
#!/usr/bin/env perl
#
# et-demo - show how to handle Estonian collation correctly
#
# Tom Christinansen <[email protected]>
# Fri Feb 22 19:27:51 MST 2013
use v5.14;
use utf8;
use strict;
use warnings;
use warnings FATAL => "utf8";
use open qw(:std :utf8);
use Unicode::Normalize;
use Unicode::Collate::Locale;
main();
exit();
sub graphemes(_) {
my($str) = @_;
my @graphs = $str =~ //X/g;
return @graphs;
}
sub same_diff($$) {
my($s1, $s2) = @_;
no locale;
if (NFC($s1) eq NFC($s2)) {
return "SAME";
} else {
return "DIFFERENT";
}
}
sub stringy {
return join("" => @_);
}
sub cp_sort {
no locale;
return sort @_;
}
sub et_sort {
state $collator = # we want Estonian here:
Unicode::Collate::Locale->new(locale => "et");
return $collator->sort(@_);
}
sub main {
my $orig = "õäöüšž ðŋц";
say " Codepoint sort: ", cp_sort(graphemes($orig));
say " Estonian sort: ", et_sort(graphemes($orig));
my $nfc = NFC($orig);
my $nfc_cp_sort = stringy cp_sort(graphemes($nfc));
my $nfc_et_sort = stringy et_sort(graphemes($nfc));
my $nfd = NFD($orig);
my $nfd_cp_sort = stringy cp_sort(graphemes($nfd));
my $nfd_et_sort = stringy et_sort(graphemes($nfd));
say "NFC/NFD sort by codepoint is ",
same_diff($nfc_cp_sort, $nfd_cp_sort);
say "NFC Codepoint sort: ", $nfc_cp_sort;
say "NFD Codepoint sort: ", $nfd_cp_sort;
say "NFC/NFD sort in estonian is ",
same_diff($nfc_et_sort, $nfd_et_sort);
say "NFC Estonian sort: ", $nfc_et_sort;
say "NFD Estonian sort: ", $nfd_et_sort;
}
Esa es realmente la forma en que debe manejar la intercalación de locale. Ver también esta respuesta para numerosos ejemplos.
Cuando use locale
, algunos caracteres de mi configuración regional (et_EE.UTF-8) no coinciden con /w
y no veo ninguna razón para ello.
Además de ASCII, Estonia usa seis caracteres más:
õäöüšž
En mi script de prueba a continuación los uso en $string
con tres caracteres especiales adicionales ðŋц
(que no pertenecen al alfabeto de Estonia).
use feature ''say'';
use POSIX qw( locale_h );
{
use utf8;
my $string = "õäöüšž ðŋц";
binmode STDOUT, ":encoding(UTF-8)";
say "nothing";
say ''LOCALE: '', setlocale(LC_CTYPE), '' '', setlocale(LC_COLLATE);
say ''UC: '', uc( $string );
say ''SORT: '', sort( split(//, $string) );
say $string =~ m//w/g;
say $string =~ m//p{Word}/g;
say '''';
}
{
use utf8;
use locale;
binmode STDOUT, ":encoding(UTF-8)";
my $string = "õäöüšž ðŋц";
say "locale";
say ''LOCALE: '', setlocale(LC_CTYPE), '' '', setlocale(LC_COLLATE);
say ''UC: '', uc( $string );
say ''SORT: '', sort( split(//, $string) );
say $string =~ m//w/g;
say $string =~ m//p{Word}/g;
say '''';
}
{
use utf8::all;
my $string = "õäöüšž ðŋц";
say "utf8::all";
say ''LOCALE: '', setlocale(LC_CTYPE), '' '', setlocale(LC_COLLATE);
say ''UC: '', uc( $string );
say ''SORT: '', sort( split(//, $string) );
say $string =~ m//w/g;
say $string =~ m//p{Word}/g;
say '''';
}
{
use utf8::all;
use locale;
my $string = "õäöüšž ðŋц";
say "utf8::all + locale";
say ''LOCALE: '', setlocale(LC_CTYPE), '' '', setlocale(LC_COLLATE);
say ''UC: '', uc( $string );
say ''SORT: '', sort( split(//, $string) );
say $string =~ m//w/g;
say $string =~ m//p{Word}/g;
say '''';
}
Intenté con Perl 5.10.1 y 5.14.2 y ambos obtuve ese resultado:
nothing
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: äðõöüŋšžц
õäöüšžðŋц
õäöüšžðŋц
locale
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: ðŋšžõäöüц
šžŋц
õäöüšžðŋц
utf8::all
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: äðõöüŋšžц
õäöüšžðŋц
õäöüšžðŋц
utf8::all + locale
LOCALE: et_EE.UTF-8 et_EE.UTF-8
UC: ÕÄÖÜŠŽ ÐŊЦ
SORT: ðŋšžõäöüц
šžŋц
õäöüšžðŋц
¿Qué no es lo que esperaba?
- principal problema: bajo
use locale
esperaba que coincidiera con mis seis caracteres, pero el resultado es bastante extraño. ¿Por qué tales partidos? De perlrecharclass leí:
Para los puntos de código por encima de 255 ... / w coincide lo mismo que / p {Word} coincide en este rango. ... Para los puntos de código por debajo de 256 ... si las reglas de configuración regional están en vigor ... / w coincide con el carácter de guión bajo nativo de la plataforma más cualquier cosa que la localidad considere que es alfanumérica.
Por lo tanto, /w
coincide con los caracteres por encima de 255, pero no coincide con "lo que la configuración regional considere que es alfanumérico". ¿Por qué? La misma hora de ordenar bajo locale funciona bien (y sin locale no), el resultado ðŋšžõäöüц
es el orden correcto, que muestra que hay caracteres derechos correctamente representados. AFAIU, el género no podría funcionar bien sin conocerlos "sea lo que sea que la localidad considere alfanumérica". ¿O?
- pensé que
setlocale
da resultado solo bajo locale-pragma. ¿Cómo puedo probar, qué configuración regional es efectiva para el alcance? - No esperaba que todos los personajes estuvieran en mayúsculas en todos los casos de prueba. AFAIU
uc
ylc
deben depender de la configuración regional. En el primer caso pensé que tendrían todos los cassettes más bajos, pero usando la configuración regional esperé que los primeros seis caracteres fueran mayúsculas y otros no. El único caso que esperé a todos los chars en mayúsculas fue el tercero. Veo que echo de menos algo importante aquí. Vaya, ahora encontré de Ic Doc: "De lo contrario, si EXPR tiene el indicador UTF-8 establecido: la semántica Unicode se usa para el cambio de caso". La bandera UTF-8 siempre está configurada en mi$string
, por lo que obtuvo una respuesta al escribirla.
Usar la locale
para la clasificación y /p{Word}
para la coincidencia es aceptable para mí, pero aún así utilizaría algunos consejos: ¿por qué /w
no funciona como esperaba?