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