[RESOLU] Tri de chaînes avec caractères accentués (dans un tableau associatif)

Administrateur PHPfrance
Administrateur PHPfrance | 11457 Messages

24 nov. 2015, 23:34

Bonjour,

Je me bats avec les tris de chaînes sur les tableaux PHP.
J'utilise un tri associatif avec la fonction asort() parce que j'en ai besoin,
mais j'ai beau définir le paramètre local sur FR,
il ne gère pas les lettres accentuées correctement.

Voici mon code :
setlocale(LC_ALL,'fr');
$tab = array('Zoé','Étienne','Alain');
asort($tab);
var_dump($tab);
Et voilà le résultat :
array (size=3)
  2 => string 'Alain' (length=5)
  0 => string 'Zoé' (length=4)
  1 => string 'Étienne' (length=8)
Bien évidemment, j'attendais Alain, Étienne puis Zoé.

Comment éduquer cette [CENSURÉ] de fonction asort(),
sachant que j'ai fait le tour de tous les paramètres possibles
et qu'il n'est pas question de réécrire Étienne en Etienne (sans accent) ?

Merci pour votre aide,

Administrateur PHPfrance
Administrateur PHPfrance | 11457 Messages

24 nov. 2015, 23:38

Et si je complète la fonction asort() avec le paramètre que je crois pourtant adapté...
setlocale(LC_ALL,'fr');
$tab= array('Zoé','Étienne','Alain');
asort($tab,SORT_LOCALE_STRING);
var_dump($tab);
... j'obtiens cela :
array (size=3)
  1 => string 'Étienne' (length=8)
  2 => string 'Alain' (length=5)
  0 => string 'Zoé' (length=4)

Mammouth du PHP | 2703 Messages

24 nov. 2015, 23:46

http://php.net/manual/fr/function.usort.php
pour utiliser une fonction de comparaison qui fonctionne comme espéré.

Administrateur PHPfrance
Administrateur PHPfrance | 11457 Messages

25 nov. 2015, 02:32

Merci pour cette piste !
En fait, c'est plutôt la fonction uasort() qu'il me faut utiliser
car j'ai besoin de conserver le caractère associatif de mon tableau.

Grâce à la contribution de [email protected] que j'ai trouvée sur php.net,
j'obtiens le résultat souhaité.
setlocale(LC_ALL,'fr');

function natksort($array) 
{ $original_keys_arr   = array(); 
  $original_values_arr = array(); 
  $clean_keys_arr      = array(); 
  $i = 0; 
  foreach($array AS $key=>$value) 
         { $original_keys_arr[$i]   = $key; 
           $original_values_arr[$i] = $value; 
           $clean_keys_arr[$i]      = strtr($key, "ÄÖÜäöüÉÈÀËëéèàç", "AOUaouEEAEeeeac"); 
           $i++; 
    	 } 
  natcasesort($clean_keys_arr); 
  $result_arr = array(); 
  foreach($clean_keys_arr AS $key=>$value) 
     	 { $original_key              = $original_keys_arr[$key]; 
           $original_value            = $original_values_arr[$key]; 
           $result_arr[$original_key] = $original_value; 
    	 } 
  return $result_arr; 
} 

$tab = array('Zoé','Étienne','Alain');
uasort($tab, 'natksort');
var_dump($tab);
Mais il subsiste toutefois deux warnings
qui attestent d'une mauvaise transmission de mon tableau $tab
et que je n'arrive pas à résoudre (la ligne 9 étant le premier foreach de la fonction).
(!) Warning: Invalid argument supplied for foreach() in W:\www\machin\test.php on line 9
Call Stack
#	Time	Memory	Function	Location
1	0.0000	245520	{main}( )	..\test.php:0
2	0.0010	246480	uasort ( )	..\test.php:26
3	0.0010	246552	natksort( )	..\test.php:26

(!) Warning: Invalid argument supplied for foreach() in W:\www\machin\test.php on line 9
Call Stack
#	Time	Memory	Function	Location
1	0.0000	245520	{main}( )	..\test.php:0
2	0.0010	246480	uasort ( )	..\test.php:26
3	0.0080	246808	natksort( )	..\test.php:26

array (size=3)
  2 => string 'Alain' (length=5)
  1 => string 'Étienne' (length=8)
  0 => string 'Zoé' (length=4)
Une idée ?

Avatar du membre
Mammouth du PHP | 1609 Messages

25 nov. 2015, 12:29

Salut, à priori la fonction de comparaison de valeurs (natksort) ne reçoit pas 1 paramètre (le tableau) mais 2 paramètres qui correspondent à 2 valeurs du tableau pour comparaison (elle est appelée plusieurs fois durant le processus). Il faut donc réécrire la fonction.

Il y a un peu plus de détail ici : http://php.net/manual/fr/function.usort.php
Développeur web depuis + de 20 ans

ViPHP
ViPHP | 2287 Messages

25 nov. 2015, 13:11

Re albat,

Tu es apparemment parti un peu rapidement du principe que natksort prendrait un seul paramètre de type array sur lequel tu pourrais boucler, ce qui est faux, comme l'a bien relevé Saian ci-dessus.

La fonction callback attendue par uasort() doit prendre deux paramètres qui sont deux éléments du tableau (sur lequel tu n'as pas à boucler, c'est builtin) sur lesquels tu effectues les opérations de ton choix pour permettre de les ordonner un à un. Cette fonction callback doit renvoyer un résultat entier, le signe de cet entier (positif ou négatif) déterminera la mise en ordre de ces deux items.

Encore un peu de pain sur la planche donc ;) Courage...
if(!@work()){ Nespresso(); } else { what(); }
______________________________

Administrateur PHPfrance
Administrateur PHPfrance | 11457 Messages

25 nov. 2015, 18:49

Humpff !
C'est pas gagné... :(

C'est quand même dingue qu'il n’existe pas une fonction pour un truc aussi simple
qu'un tri alphabétique prenant en compte les caractères altérés des diverses langues ! :evil:

À moins qu'il faille pour cela attendre l'intégration d'UTF-8.... dans PHP 6 ? (drôôôôôôôle) :-*

Administrateur PHPfrance
Administrateur PHPfrance | 11457 Messages

25 nov. 2015, 20:52

Bon...
Comme je ramais la mer sans avoir l'impression d'avancer,
j'ai redemandé à Google de me donner un coup de main.

Et en tripotant un script trouvé je-ne-sais-plus-où, ben... j'ai réussi !!! \:D/

Et c'est même tout simple:
function compare_sansaccent($a, $b)
{ return strcmp(suppr_accents($a), suppr_accents($b));
}

uasort($tab, 'compare_sansaccent');
Pour la fonction suppr_accents($chaine), rien de bien sorcier,
c'est une succession de str_replace(), la fonction strtr() foirant en UTF-8.

Mammouth du PHP | 571 Messages

25 nov. 2015, 20:59

il y a une extension php d'internationalisation appelée intl qui te permet la gestion des locales.Pour l'installer sous debian par exemple: apt-get install php5-intl
$tab= array('Zoé','Étienne','Alain');
$coll = collator_create( 'fr_FR' );

collator_asort( $coll, $tab, Collator::SORT_STRING);
var_dump($tab);

Enzo
Invité n'ayant pas de compte PHPfrance

07 févr. 2019, 18:33

Autre exemple, permettant de se passer d'un série de str_replace :


setlocale(LC_ALL, 'fr_FR.UTF8');

function utfsort(&$arr) {
function utfcmp($a, $b) {
return strcmp(iconv('UTF-8', 'ASCII//TRANSLIT', $a), iconv('UTF-8', 'ASCII//TRANSLIT', $b));
}
usort($arr, 'utfcmp');
}

utfsort($mon_tableau);