Я создаю программу для сортировки указателей книг на разных языках. Он использует Perl и отключает локаль. Я разрабатываю его для Unix, но он должен быть переносимым на Windows. Должно ли это работать в принципе, или, полагаясь на локаль, я ошибаюсь? Суть в том, что мне действительно нужно, чтобы это работало под Windows, но мне удобнее разрабатывать в своей среде UNIX.
Многоязычная сортировка текста в Perl, в Windows, с использованием локали
Ответы (2)
Предполагая, что вашей отправной точкой является Unicode, поскольку вы очень тщательно декодировали все входящие данные, независимо от того, какая может быть их исходная кодировка, тогда легко использовать модуль Unicode::Collate в качестве отправной точки.
Если вы хотите настроить локаль, то вы, вероятно, захотите начать с Unicode::Collate::Locale вместо этого.
Декодирование в Юникод
Если вы работаете в среде, полностью поддерживающей UTF8, это несложно, но если вы подвержены превратностям случайных так называемых «локалей» (или, что еще хуже, уродливых вещей, которые Microsoft называет «кодовыми страницами»), вам может понадобиться чтобы получить модуль CPAN Encode::Locale, чтобы помочь вам. Например:
use Encode;
use Encode::Locale;
# use "locale" as an arg to encode/decode
@ARGV = map { decode(locale => $_) } @ARGV;
# or as a stream for binmode or open
binmode $some_fh, ":encoding(locale)";
binmode STDIN, ":encoding(console_in)" if -t STDIN;
binmode STDOUT, ":encoding(console_out)" if -t STDOUT;
binmode STDERR, ":encoding(console_out)" if -t STDERR;
(Если бы это был я, я бы просто использовал ":utf8" для вывода.)
Стандартная сортировка, а также локали и адаптация
Дело в том, что как только вы все декодируете во внутренний формат Perl, вы можете использовать на нем Unicode::Collate и Unicode::Collate::Locale. Это может быть очень просто:
use v5.14;
use utf8;
use Unicode::Collate;
my @exes = qw( x⁷ x⁰ x⁸ x³ x⁶ x⁵ x⁴ x² x⁹ x¹ );
@exes = Unicode::Collate->new->sort(@exes);
say "@exes";
# prints: x⁰ x¹ x² x³ x⁴ x⁵ x⁶ x⁷ x⁸ x⁹
Или они могут быть довольно причудливыми. Вот один из них, который пытается работать с названиями книг: он удаляет начальные статьи и цифры с нулями.
my $collator = Unicode::Collate->new(
--upper_before_lower => 1,
--preprocess => {
local $_ = shift;
s/^ (?: The | An? ) \h+ //x; # strip articles
s/ ( \d+ ) / sprintf "%020d", $1 /xeg;
return $_;
};
);
Теперь просто используйте метод sort этого объекта для сортировки.
Иногда нужно вывернуть сортировку наизнанку. Например:
my $collator = Unicode::Collate->new();
for my $rec (@recs) {
$rec->{NAME_key} =
$collator->getSortKey( $rec->{NAME} );
}
@srecs = sort {
$b->{AGE} <=> $a->{AGE}
||
$a->{NAME_key} cmp $b->{NAME_key}
} @recs;
Причина, по которой вы должны это сделать, заключается в том, что вы сортируете запись с различными полями. Ключ двоичной сортировки позволяет вам использовать оператор cmp для данных, которые прошли через выбранный вами/пользовательский объект подборки.
Полный конструктор для объекта подборки имеет все это для формального синтаксиса:
$Collator = Unicode::Collate->new(
UCA_Version => $UCA_Version,
alternate => $alternate, # alias for 'variable'
backwards => $levelNumber, # or \@levelNumbers
entry => $element,
hangul_terminator => $term_primary_weight,
highestFFFF => $bool,
identical => $bool,
ignoreName => qr/$ignoreName/,
ignoreChar => qr/$ignoreChar/,
ignore_level2 => $bool,
katakana_before_hiragana => $bool,
level => $collationLevel,
minimalFFFE => $bool,
normalization => $normalization_form,
overrideCJK => \&overrideCJK,
overrideHangul => \&overrideHangul,
preprocess => \&preprocess,
rearrange => \@charList,
rewrite => \&rewrite,
suppress => \@charList,
table => $filename,
undefName => qr/$undefName/,
undefChar => qr/$undefChar/,
upper_before_lower => $bool,
variable => $variable,
);
Но обычно вам не нужно беспокоиться ни о каком из них. На самом деле, если вы хотите настроить локаль для конкретной страны с использованием данных CLDR, вы должны просто использовать Unicode::Collate::Locale, который добавляет в конструктор еще ровно один параметр: locale => $country_code.
use Unicode::Collate::Locale;
$coll = Unicode::Collate::Locale->
new(locale => "fr");
@french_text = $coll->sort(@french_text);
Видите, как это легко?
Но вы можете делать и другие интересные вещи.
use Unicode::Collate::Locale;
my $Collator = new Unicode::Collate::Locale::
locale => "de__phonebook",
level => 1,
normalization => undef,
;
my $full = "Ich müß Perl studieren.";
my $sub = "MUESS";
if (my ($pos,$len) = $Collator->index($full, $sub)) {
my $match = substr($full, $pos, $len);
say "Found match of literal ‹$sub› in ‹$full› as ‹$match›";
}
При запуске пишет:
Found match of literal ‹MUESS› in ‹Ich müß Perl studieren.› as ‹müß›
Вот доступные локали для v0.96 модуля Unicode::Collate::Locale, взятые с его справочной страницы:
locale name description
--------------------------------------------------------------
af Afrikaans
ar Arabic
as Assamese
az Azerbaijani (Azeri)
be Belarusian
bg Bulgarian
bn Bengali
bs Bosnian
bs_Cyrl Bosnian in Cyrillic (tailored as Serbian)
ca Catalan
cs Czech
cy Welsh
da Danish
de__phonebook German (umlaut as 'ae', 'oe', 'ue')
ee Ewe
eo Esperanto
es Spanish
es__traditional Spanish ('ch' and 'll' as a grapheme)
et Estonian
fa Persian
fi Finnish (v and w are primary equal)
fi__phonebook Finnish (v and w as separate characters)
fil Filipino
fo Faroese
fr French
gu Gujarati
ha Hausa
haw Hawaiian
hi Hindi
hr Croatian
hu Hungarian
hy Armenian
ig Igbo
is Icelandic
ja Japanese [1]
kk Kazakh
kl Kalaallisut
kn Kannada
ko Korean [2]
kok Konkani
ln Lingala
lt Lithuanian
lv Latvian
mk Macedonian
ml Malayalam
mr Marathi
mt Maltese
nb Norwegian Bokmal
nn Norwegian Nynorsk
nso Northern Sotho
om Oromo
or Oriya
pa Punjabi
pl Polish
ro Romanian
ru Russian
sa Sanskrit
se Northern Sami
si Sinhala
si__dictionary Sinhala (U+0DA5 = U+0DA2,0DCA,0DA4)
sk Slovak
sl Slovenian
sq Albanian
sr Serbian
sr_Latn Serbian in Latin (tailored as Croatian)
sv Swedish (v and w are primary equal)
sv__reformed Swedish (v and w as separate characters)
ta Tamil
te Telugu
th Thai
tn Tswana
to Tonga
tr Turkish
uk Ukrainian
ur Urdu
vi Vietnamese
wae Walser
wo Wolof
yo Yoruba
zh Chinese
zh__big5han Chinese (ideographs: big5 order)
zh__gb2312han Chinese (ideographs: GB-2312 order)
zh__pinyin Chinese (ideographs: pinyin order) [3]
zh__stroke Chinese (ideographs: stroke order) [3]
zh__zhuyin Chinese (ideographs: zhuyin order) [3]
Locales according to the default UCA rules include chr (Cherokee), de (German), en (English), ga (Irish), id (Indonesian),
it (Italian), ka (Georgian), ms (Malay), nl (Dutch), pt (Portuguese), st (Southern Sotho), sw (Swahili), xh (Xhosa), zu
(Zulu).
Note
[1] ja: Ideographs are sorted in JIS X 0208 order. Fullwidth and halfwidth forms are identical to their regular form. The
difference between hiragana and katakana is at the 4th level, the comparison also requires "(variable => 'Non-ignorable')",
and then "katakana_before_hiragana" has no effect.
[2] ko: Plenty of ideographs are sorted by their reading. Such an ideograph is primary (level 1) equal to, and secondary
(level 2) greater than, the corresponding hangul syllable.
[3] zh__pinyin, zh__stroke and zh__zhuyin: implemented alt='short', where a smaller number of ideographs are tailored.
Note: 'pinyin' is in latin, 'zhuyin' is in bopomofo.
Таким образом, основной трюк состоит в том, чтобы декодировать ваши локальные данные в единообразное представление Unicode, а затем использовать детерминированную сортировку, возможно, адаптированную, которая не зависит от случайных настроек окна консоли пользователя для правильного поведения.
Примечание: все эти примеры, за исключением ссылки на справочную страницу, любезно взяты из 4го издания Programming Perl с любезного разрешения его автора. :)
Win32::OLE::NLS предоставляет вам доступ к этой части системы. Он предоставляет вам CompareString и необходимые инструменты для получения необходимого идентификатора локали.
Если вы хотите/нужно найти системную документацию, базовый системный вызов называется CompareStringEx.
cmp я получу таким образом, чтобы он не действовал по-разному в разных системах. Смотрите мой ответ, как это сделать.
- person tchrist; 22.02.2013
Unicode::Collate::Locale, которые почти бесконечно лучше, чем локали LC_blah.
- person tchrist; 22.02.2013