(PHP 4, PHP 5, PHP 7, PHP 8)
strnatcmp — Comparación de strings con el algoritmo de "orden natural"
Implementa el algoritmo de comparación que ordena los strings como lo haría un ser humano. Tenga en cuenta que esta comparación distingue entre mayúsculas y minúsculas.
string1El primer string.
string2El segundo string.
Devuelve un valor inferior a 0 si string1
es inferior a string2; un valor superior
a 0 si string1 es superior a
string2, y 0 si son
iguales. No
se puede deducir ningún significado particular de este valor,
excepto su signo.
| Versión | Descripción |
|---|---|
| 8.2.0 |
Esta función ya no garantiza retornar
strlen($string1) - strlen($string2) cuando las longitudes de
las strings no son iguales, y puede retornar -1 o
1 en su lugar.
|
Un ejemplo de la diferencia de tratamiento con el algoritmo estándar se presenta a continuación:
Ejemplo #1 strcmp()
<?php
$arr1 = $arr2 = array("img12.png", "img10.png", "img2.png", "img1.png");
echo "Ordenación de strings estándar\n";
usort($arr1, "strcmp");
print_r($arr1);
echo "\nOrdenación de strings \"orden natural\"\n";
usort($arr2, "strnatcmp");
print_r($arr2);
?>El ejemplo anterior mostrará:
Ordenación de strings estándar Array ( [0] => img1.png [1] => img10.png [2] => img12.png [3] => img2.png ) Ordenación de strings "orden natural" Array ( [0] => img1.png [1] => img2.png [2] => img10.png [3] => img12.png )
Natural Order String
Comparison de Martin Pool (en inglés).
Can also be used with combination of a compare for an array nested value, like
<?php
$array = array(
"city" => "xyz",
"names" => array(
array(
"name" => "Ana2",
"id" => 1
) ,
array(
"name" => "Ana1",
"id" => 2
)
)
);
usort($array["names"], function ($a, $b) { return strnatcmp($a['name'], $b['name']);} );There seems to be a bug in the localization for strnatcmp and strnatcasecmp. I searched the reported bugs and found a few entries which were up to four years old (but the problem still exists when using swedish characters).
These functions might work instead.
<?php
function _strnatcasecmp($left, $right) {
return _strnatcmp(strtolower($left), strtolower($right));
}
function _strnatcmp($left, $right) {
while((strlen($left) > 0) && (strlen($right) > 0)) {
if(preg_match('/^([^0-9]*)([0-9].*)$/Us', $left, $lMatch)) {
$lTest = $lMatch[1];
$left = $lMatch[2];
} else {
$lTest = $left;
$left = '';
}
if(preg_match('/^([^0-9]*)([0-9].*)$/Us', $right, $rMatch)) {
$rTest = $rMatch[1];
$right = $rMatch[2];
} else {
$rTest = $right;
$right = '';
}
$test = strcmp($lTest, $rTest);
if($test != 0) {
return $test;
}
if(preg_match('/^([0-9]+)([^0-9].*)?$/Us', $left, $lMatch)) {
$lTest = intval($lMatch[1]);
$left = $lMatch[2];
} else {
$lTest = 0;
}
if(preg_match('/^([0-9]+)([^0-9].*)?$/Us', $right, $rMatch)) {
$rTest = intval($rMatch[1]);
$right = $rMatch[2];
} else {
$rTest = 0;
}
$test = $lTest - $rTest;
if($test != 0) {
return $test;
}
}
return strcmp($left, $right);
}
?>
The code is not optimized. It was just made to solve my problem.This function has some interesting behaviour on strings consisting of mixed numbers and letters.
One may expect that such a mixed string would be treated as alpha-numeric, but that is not true.
var_dump(strnatcmp('23','123')); →
int(-1)
As expected, 23<123 (even though first digit is higher, overall number is smaller)
var_dump(strnatcmp('yz','xyz')); →
int(1)
As expected, yz>xyz (string comparison, irregardless of string length)
var_dump(strnatcmp('2x','12y')); →
int(-1)
Remarkable, 2x<12y (does a numeric comparison)
var_dump(strnatcmp('20x','12y'));
int(1)
Remarkable, 20x>12y (does a numeric comparison)
It seems to be splitting what is being compared into runs of numbers and letters, and then comparing each run in isolation, until it has an ordering difference.Some more remarkable outcomes:
var_dump(strnatcmp("0.15m", "0.2m"));
int(1)
var_dump(strnatcmp("0.15m", "0.20m"));
int(-1)
It's not about localisation:
var_dump(strnatcmp("0,15m", "0,2m"));
int(1)
var_dump(strnatcmp("0,15m", "0,20m"));
int(-1)