2013-08-16 16 views
7

Sto usando strnatcmp nella mia funzione di confronto per ordinare i nomi delle persone in una tabella. Per il nostro cliente belga, otteniamo alcuni strani risultati. Hanno nomi come "Van der Broecke" e "Vander Veere", e strnatcasecmp("Van der", "Vander") restituisce 0!Perché gli spazi vengono ignorati in natsort/strnatcmp/strnatcasecmp?

Poiché il confronto naturale mira a ordinare come un essere umano, non capisco perché gli spazi siano completamente ignorati.

Esempio:

$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); 

Dà:

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 
) 

Ma un essere umano direbbe:

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 
) 

La mia soluzione è ora quello di sostituire tutti gli spazi con sottolineature, che vengono gestiti correttamente . Due domande: Perché lo standard natsort funziona in questo modo? Esiste una soluzione migliore?

+0

Mi pare di capire, 'natsort' ordina le stringhe da numeri come gli esseri umani, esso doesn 'ordinare in modo corretto se non ci sono numeri – baldrs

+0

@Baldrs No. PHP.net: "Questa funzione implementa un algoritmo di ordinamento che ordina le stringhe alfanumeriche nel modo in cui un essere umano vorrebbe mantenere le associazioni chiave/valore." – Spork

+0

Una frase chiave è ** stringhe alfanumeriche **. – baldrs

risposta

2

Se si guarda nel codice sorgente si può effettivamente vedere questo, che sicuramente sembra un bug: http://gcov.php.net/PHP_5_3/lcov_html/ext/standard/strnatcmp.c.gcov.php (scorrere verso il basso per la linea 130):

//inside a while loop... 

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

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

Si noti che è un collegamento a 5,3, ma lo stesso codice esiste ancora in 5.5 (http://gcov.php.net/PHP_5_5/lcov_html/ext/standard/strnatcmp.c.gcov.php) Devo ammettere che la mia conoscenza di C è limitata, ma sembra che stia avanzando il puntatore su ogni stringa se il carattere corrente è uno spazio, ignorando essenzialmente quel carattere nell'ordinamento. Il commento implica che lo farà solo se gli spazi bianchi sono consecutivi; tuttavia, non vi è alcun controllo per garantire che il personaggio precedente fosse in realtà uno spazio prima. Ciò avrebbe bisogno di qualcosa come

//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)); 

Qualcuno che sa davvero C potrebbe probabilmente scrivere questo meglio, ma questa è l'idea generale.

Su un'altra nota potenzialmente interessante, per il tuo esempio, se stai usando PHP> = 5.4, questo dà lo stesso risultato come l'usort menzionato da Aaron Saray (che fa perdere le associazioni chiave/valore così):

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

Molto interessante! Ci scusiamo per aver ignorato questa domanda per un po '- ero fuori dal giro per un po'. – Spork

+0

Grazie - se questo è quello che stavi cercando ti dispiacerebbe accettare la risposta? – ChicagoRedSox

2

Dai un'occhiata a bugs.php.net # 26412 (natsort() comprime più spazi in 1 spazio). Apparentemente, questo comportamento è così "aa", "a a" e "a a" (nota i 2 spazi) non ordinate come stringhe identiche.

+0

Un bug report non accettato dal 2003! :) Ma è, in effetti, lo stesso problema di base .. Ma io non capisco la tua conclusione del perché questo sarebbe. Sembrano fare l'opposto di me: sort aa, a a e a a identici, questi ultimi sono assolutamente identici, e il primo è più vicino a un a che a un ab. È ancora più complesso Kolmogorovian aggiungere uno spazio e un personaggio ... Sono ancora perplesso – Spork

1

Come altre risposte/commentatori hanno detto, questo è un problema noto. Tuttavia, puoi scrivere il tuo tipo con usort(). Si prega di provare questo e vedere se funziona:

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

ho notato l'uscita è leggermente diversa rispetto la risposta suggerita:

È suggerito:

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

Ma sono sicuro che questo è stato un errore di battitura - questi dovrebbero essere scambiati. :)

Problemi correlati