perl sorting unicode utf-8 locale

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 y lc 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?