2010-10-20 53 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 

我不能真正遵循:在同時,在使用現場,有一個< b 一個@雅虎。 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有或沒有本地化模塊比我曾經信任過的供應商本地化模塊多一百萬倍。那些對我來說總是失敗。現在我們已經有了Unicode,所以我非常強烈地認爲/ opine語言環境不適用於ctype/collat​​e目的的傳統bandaides。 – tchrist 2011-08-27 15:11:03

+0

不,我正在描述他的系統的實際情況,因爲它適用於他的問題。我懷疑現在大多數供應商都是基於UCA實施他們的區域設置;畢竟UCA不是憑空發明的。但我沒有看到你的觀點爲什麼區域設置失敗或應該被視爲遺留問題。當然,只要語言偏離「默認」排序,您就需要語言特定的排序規則。但這個問題的確非常重要。 – 2011-08-28 09:03:08

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模塊訪問)返回一個表示對照順序的字符串。當比較兩個這樣的轉換後的字節時,第一個字節爲小於第一個字節的排序順序排在第一位。

我不確定這是否是一個錯誤。我似乎無法找到關於en_US排序順序應該如何工作的任何文檔。如果它一個錯誤,它在你的C庫或語言環境數據庫中。

+0

聽起來像一個錯誤,可能是一個有意識的人知道glibc開發人員... – 2010-10-21 05:53:05

+0

我懷疑上述問題與以下問題有關:在一個簡單的文件包含2記錄與2個TAB分隔的字段,如'a_2 2/a2 1'命令就像'sort -k 1 file | cut -f 1'將顯示與相同排序顯示的順序相反的順序,但不顯示第二個字段。 – Krambambuli 2010-10-21 10:21:11