2013-08-16 24 views
7

我在我的比較函數中使用strnatcmp來排序表中的人名。對於我們的比利時客戶,我們得到了一些奇怪的結果。他們有'範德布勒克'和'範德維爾'的名字,並且strnatcasecmp("Van der", "Vander")返回0!由於自然比較的目的是將人類排序,所以我不明白爲什麼這些空間完全被忽視。爲什麼在natsort/strnatcmp/strnatcasecmp中忽略空格?

如:

$names = array("Van de broecke", "Vander Veere", "Vande Muizen", "Vander Zoeker", "Van der Programma", "vande Huizen", "vande Kluizen", "vander Muizen", "Van der Luizen"); 
natcasesort($names); 

print_r($names); 

給出:

Array ( 
[0] => Van de broecke 
[5] => vande Huizen 
[6] => vande Kluizen 
[2] => Vande Muizen 
[8] => Van der Luizen 
[7] => vander Muizen 
[4] => Van der Programma 
[1] => Vander Veere 
[3] => Vander Zoeker 
) 

但人會說:

Array ( 
[0] => Van de broecke 
[4] => Van der Programma 
[8] => Van der Luizen 
[5] => vande Huizen 
[6] => vande Kluizen 
[2] => Vande Muizen 
[7] => vander Muizen 
[1] => Vander Veere 
[3] => Vander Zoeker 
) 

我的解決辦法,現在是更換用下劃線的所有空間,這得到妥善處理。兩個問題: 爲什麼natsort這樣工作? 有更好的解決方案嗎?

+0

據我瞭解,'natsort'用數字爲人類做排序的字符串,它不如果沒有數字,則排序正確 – baldrs

+0

@Baldrs No. PHP.net:「該函數實現了一種排序算法,該算法按照人類的方式對字母數字字符串進行排序,同時保持鍵/值關聯。」 – Spork

+0

關鍵短語是**字母數字字符串**。 – baldrs

回答

2

如果您在源代碼中查找你可以看到這一點,這肯定似乎是一個錯誤: http://gcov.php.net/PHP_5_3/lcov_html/ext/standard/strnatcmp.c.gcov.php(向下滾動到線130):

//inside a while loop... 

/* Skip consecutive whitespace */ 
while (isspace((int)(unsigned char)ca)) { 
     ca = *++ap; 
} 

while (isspace((int)(unsigned char)cb)) { 
     cb = *++bp; 
} 

注意,這是一個鏈接到5.3,但相同的代碼仍然存在於5.5(http://gcov.php.net/PHP_5_5/lcov_html/ext/standard/strnatcmp.c.gcov.php) 不可否認,我對C語言的瞭解是有限的,但是如果當前字符是空格,基本上忽略了該字符,這基本上看起來是推進了每個字符串上的指針。該評論意味着只有在空格爲連續時才這樣做;然而,沒有檢查來確保先前的字符實際上是第一個空格。這需要類似

//declare these outside the loop 
short prevAIsSpace = 0; 
short prevBIsSpace = 0; 

//....in the loop 
while (prevAIsSpace && isspace((int)(unsigned char)ca)) { 
    //won't get here the first time since prevAIsSpace == 0 
    ca = *++ap; 
} 
//now if the character is a space, flag it for the next iteration 
prevAIsSpace = isspace((int)(unsigned char)ca)); 
//repeat with string b 
while (prevBIsSpace && isspace((int)(unsigned char)cb)) { 
    cb = *++bp; 
} 
prevBIsSpace = isspace((int)(unsigned char)cb)); 

有人誰真正知道C可能可以寫得更好,但這是一般的想法。

在另一個有趣的筆記上,例如,如果您使用PHP> = 5。4,這給了相同的結果亞倫薩拉伊提到的usort(它失去了鍵/值協會以及):

sort($names, SORT_FLAG_CASE | SORT_STRING); 

print_r($names); 
Array ( 
    [0] => Van de broecke 
    [1] => Van der Luizen 
    [2] => Van der Programma 
    [3] => vande Huizen 
    [4] => vande Kluizen 
    [5] => Vande Muizen 
    [6] => vander Muizen 
    [7] => Vander Veere 
    [8] => Vander Zoeker 
) 
+0

非常有趣!對不起,我忽略了這個問題一段時間 - 我退出了一段循環。 – Spork

+0

謝謝 - 如果這是你正在尋找你會介意接受答案? – ChicagoRedSox

2

看看bugs.php.net#26412(natsort()將多個空間壓縮到1個空間)。顯然,這種行爲是如此「aa」,「a」和「a」(注意2個空格)不排序爲相同的字符串。

+0

2003年的一個未被接受的錯誤報告! :)但事實上,它是相同的核心問題。 但我不明白你爲什麼會這樣做的結論。他們似乎做了與我相反的事情:排序aa,a和a相同,後兩者完全相同,前者更接近a而不是ab。它是更加Kolmogorovian複雜增加一個空間和一個字符... 我仍然困惑 – Spork

1

像其他答案/評論者所說,這是一個已知的問題。但是,您可以使用usort()編寫自己的排序。請試試這個,看看它的工作原理:

usort($names2, function($first, $second) { 
    if ($first == $second) { 
     return 0; 
    } 
    else { 
     return (strtolower($first) < strtolower($second)) ? -1 : 1; 
} 
}); 

我注意到輸出比你所建議的答案稍有不同:

您建議:

[4] => Van der Programma 
[8] => Van der Luizen 

但我敢肯定,這是一個錯字 - 這些應該交換。 :)