strnatcmp

(PHP 4, PHP 5, PHP 7)

strnatcmp使用自然排序算法比较字符串

说明

strnatcmp ( string $str1 , string $str2 ) : int

该函数实现了以人类习惯对数字型字符串进行排序的比较算法,这就是"自然顺序"。注意该比较区分大小写。

参数

str1

第一个字符串。

str2

第二个字符串。

返回值

与其他字符串比较函数类似,如果 str1 小于 str2 返回 < 0; 如果 str1 大于 str2 返回 > 0;如果两者相等,返回 0。

范例

下面的例子展示了该算法与计算机常规字符串比较算法( strcmp() 所使用的)的区别:

<?php
$arr1 
$arr2 = array("img12.png""img10.png""img2.png""img1.png");
echo 
"Standard string comparison\n";
usort($arr1"strcmp");
print_r($arr1);
echo 
"\nNatural order string comparison\n";
usort($arr2"strnatcmp");
print_r($arr2);
?>

以上例程会输出:

Standard string comparison
Array
(
    [0] => img1.png
    [1] => img10.png
    [2] => img12.png
    [3] => img2.png
)

Natural order string comparison
Array
(
    [0] => img1.png
    [1] => img2.png
    [2] => img10.png
    [3] => img12.png
)
更多信息,参见:Martin Pool 的» 自然顺序的字符串比较 page.

参见

  • preg_match() - 执行匹配正则表达式
  • strcasecmp() - 二进制安全比较字符串(不区分大小写)
  • substr() - 返回字符串的子串
  • stristr() - strstr 函数的忽略大小写版本
  • strcmp() - 二进制安全字符串比较
  • strncmp() - 二进制安全比较字符串开头的若干个字符
  • strncasecmp() - 二进制安全比较字符串开头的若干个字符(不区分大小写)
  • strnatcasecmp() - 使用“自然顺序”算法比较字符串(不区分大小写)
  • strstr() - 查找字符串的首次出现
  • natsort() - 用“自然排序”算法对数组排序
  • natcasesort() - 用“自然排序”算法对数组进行不区分大小写字母的排序

User Contributed Notes

spamspamspam at gmx dot com 30-Oct-2018 04:24
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)
chris at ocproducts dot com 04-May-2017 01:16
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.
in dot games dot mq at gmail dot com 12-Dec-2016 03:34
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']);} );
Zero 04-Dec-2010 02:14
If you want to compare $_SESSION variables to a string use this

<?php
if(isset($_SESSION['usertype']))
{   
     echo
"usertype " .$_SESSION['usertype'];
               
    
$ut = $_SESSION['usertype'];
               
     if(
strnatcmp($ut,"admin"))
     {
          echo
"hello admin";
     }
}
?>
thomas at uninet dot se 25-Jul-2006 12:50
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.