2010-10-20 1 views
5

Следующий кодПочему добавление общего суффикса отменяет порядок сортировки в локали en_US?

#!/usr/bin/perl 

use strict; 
use warnings; 

my $s1 = '[email protected]'; 
my $s2 = '[email protected]'; 
my $s3 = 'aaa2000'; 
my $s4 = 'aaa_2000'; 

no locale; 

print "\nNO Locale:\n\n"; 

if ($s1 gt $s2) {print "$s1 is > $s2\n";} 
if ($s1 lt $s2) {print "$s1 is < $s2\n";} 
if ($s1 eq $s2) {print "$s1 is = $s2\n";} 

if ($s3 gt $s4) {print "$s3 is > $s4\n";} 
if ($s3 lt $s4) {print "$s3 is < $s4\n";} 
if ($s3 eq $s4) {print "$s3 is = $s4\n";} 

use locale; 

print "\nWith 'use locale;':\n\n"; 

if ($s1 gt $s2) {print "$s1 is > $s2\n";} 
if ($s1 lt $s2) {print "$s1 is < $s2\n";} 
if ($s1 eq $s2) {print "$s1 is = $s2\n";} 

if ($s3 gt $s4) {print "$s3 is > $s4\n";} 
if ($s3 lt $s4) {print "$s3 is < $s4\n";} 
if ($s3 eq $s4) {print "$s3 is = $s4\n";} 

распечатывает

NO Locale: 

[email protected] is < [email protected] 
aaa2000 is < aaa_2000 

With 'use locale;': 

[email protected] is > [email protected] 
aaa2000 is < aaa_2000 

, который я не могу следовать: в же время при использовании локали, есть < б И A @ Yahoo. com> [email protected]? !!

Я пропустил что-то более или менее очевидное или это ошибка? Могут ли другие подтвердить, что они видят такое же поведение?

Locale is $ locale 
LANG=en_US.UTF-8 
LC_CTYPE="en_US.UTF-8" 
LC_NUMERIC="en_US.UTF-8" 
LC_TIME="en_US.UTF-8" 
LC_COLLATE="en_US.UTF-8" 
LC_MONETARY="en_US.UTF-8" 
LC_MESSAGES="en_US.UTF-8" 
LC_PAPER="en_US.UTF-8" 
LC_NAME="en_US.UTF-8" 
LC_ADDRESS="en_US.UTF-8" 
LC_TELEPHONE="en_US.UTF-8" 
LC_MEASUREMENT="en_US.UTF-8" 
LC_IDENTIFICATION="en_US.UTF-8" 
LC_ALL= 

Спасибо заранее.

ответ

4

С включенными локалями сортировка выполняется в несколько проходов. Каждый персонаж имеет четыре веса, которые сравниваются в последовательных проходах. Знаки @ и _, как и большинство знаков препинания, не имеют первичного, вторичного или третичного веса, поэтому они вступают в игру только на четвертом проходе. Так, для примера

[email protected] > [email protected] 

в первом проходе, это действительно сравнивая

aaa2000yahoocom = aaa2000yahoocom 

, а затем в четвертом проходе (нет дифференцирующих факторов во втором и третьих проходах)

@. > [email protected] 

потому что @ оказывается в этом регионе не более _. (Это просто выбор, который делает определение локали, по-видимому, основано на каком-то стандарте ISO или другом.)

Вы можете заглянуть в детали реализации этого. Сопоставление локали с поддержкой заканчивается в библиотеке C как strxfrm(A) cmp strxfrm(B).Запустить эту программу:

use POSIX; 

my $s1 = '[email protected]'; 
my $s2 = '[email protected]'; 

foreach ($s1, $s2) { 
    printf "%s =>\t%v02x\n", $_, POSIX::strxfrm($_); 
} 

я получаю:

[email protected] => 0c.0c.0c.04.02.02.02.24.0c.13.1a.1a.0e.1a.18.01.08.08.08.08.08.08.08.08.08.08.08.08.08.08.08.01.02.02.02.02.02.02.02.02.02.02.02.02.02.02.02.01.08.5d.06.44 
# explanation:   a a a 2 0 0 0 y a h o o c o m DIV secondary weights ...      DIV tertiary weights ...      DIV @  . 
[email protected] => 0c.0c.0c.04.02.02.02.24.0c.13.1a.1a.0e.1a.18.01.08.08.08.08.08.08.08.08.08.08.08.08.08.08.08.01.02.02.02.02.02.02.02.02.02.02.02.02.02.02.02.01.04.36.05.5d.06.44 
# explanation:   a a a 2 0 0 0 y a h o o c o m DIV secondary weights ...      DIV tertiary weights ...      DIV _  @  . 

Путь получены эти цифры является деталью реализации; они просто должны выходить так, что сравнение по байтам дает желаемый конечный результат. Но концепция одинакова во всех современных средах программирования с возможностью сортировки по локали.

+0

Вы просто описываете алгоритм сортировки Unicode. Это не обычная вещь. Но я бы доверял UCA с моделями локалей или без них в миллион раз больше, чем когда-либо доверял локаторам поставщиков. Те, кто всегда меня терпят неудачи. Я очень сильно считаю/opine locales устаревшими бандажидами для целей ctype/collate, теперь у нас есть Unicode. – tchrist

+0

Нет, я описываю реальность его системы, поскольку она применима к его проблеме. Я подозреваю, что большинство поставщиков в настоящее время внедряют свои локали на основе UCA; ведь UCA не изобретен из воздуха. Но я не вижу вашего мнения о том, почему локали терпят неудачу или их следует считать наследием. Конечно, вам нужны правила сортировки по языку, если существуют языки, которые отклоняются от порядка по умолчанию. Но это действительно к тому же вопрос. –

2

Я получаю те же результаты в своей 32-разрядной системе Linux с локальностью en_US.utf8. Это не ошибка Perl, как показано в этой программе C:

#include <locale.h> 
#include <string.h> 
#include <stdio.h> 

void transformed(const char* str) 
{ 
    char dest[256]; 
    const char* c; 

    strxfrm(dest, str, sizeof(dest)); 
    printf("%18s =", str); 
    for (c = dest; *c; ++c) printf(" %02x", *c); 
    puts(""); 
} /* end transformed */ 

void test_strings(const char* s1, const char* s2) 
{ 
    int c = strcoll(s1, s2); 

    printf("%s is %s %s\n", s1, ((c < 0) ? "<" : ((c == 0) ? "=" : ">")), s2); 
} /* end test_strings */ 

int main(int argc, char* argv[]) 
{ 
    puts("with C locale:"); 

    test_strings("[email protected]", "[email protected]"); 
    test_strings("aaa2000", "aaa_2000"); 

    setlocale(LC_ALL, ""); 
    puts("\nwith your locale:"); 

    test_strings("[email protected]", "[email protected]"); 
    test_strings("aaa2000", "aaa_2000"); 
    puts(""); 
    transformed("[email protected]"); 
    transformed("[email protected]"); 
    transformed("aaa2000"); 
    transformed("aaa_2000"); 
    return 0; 
} /* end main */ 

С LANG=en_US.utf8, он генерирует:

with C locale: 
[email protected] is < [email protected] 
aaa2000 is < aaa_2000 

with your locale: 
[email protected] is > [email protected] 
aaa2000 is < aaa_2000 

[email protected] = 0c 0c 0c 04 02 02 02 24 0c 13 1a 1a 0e 1a 18 01 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 01 08 5d 06 44 
[email protected] = 0c 0c 0c 04 02 02 02 24 0c 13 1a 1a 0e 1a 18 01 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 02 02 02 02 02 02 02 02 01 04 36 05 5d 06 44 
      aaa2000 = 0c 0c 0c 04 02 02 02 01 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 
      aaa_2000 = 0c 0c 0c 04 02 02 02 01 08 08 08 08 08 08 08 01 02 02 02 02 02 02 02 01 04 36 

strxfrm функция (которую вы можете получить доступ в Perl через POSIX модуль) возвращает строка, которая указывает порядок сортировки. Когда вы сравниваете два таких преобразованных строки byte-for-byte, первый, у которого есть младший байт, сначала берется в порядке сортировки.

Я не уверен, что это ошибка или нет. Кажется, я не могу найти документацию о том, как должен работать порядок сортировки en_US. Если это , это ошибка, это в вашей библиотеке C или языковой базе.

+0

Звучит как ошибка, возможно, намеренная, зная разработчиков glibc ... –

+0

Я подозреваю, что вышеупомянутая проблема связана со следующим: в простой файл, содержащий 2 записи с двумя полями, разделенными TAB, например 'a_2 2/a2 1' команда вроде 'sort -k 1 file | cut -f 1 'будет отображать обратный порядок, чем тот, который показан в том же роде, но в файле, не имеющем второго поля. – Krambambuli

Смежные вопросы