我有一组数字、特殊字符和字母。我如何对它进行排序,首先是步行 digist,然后是特殊的字符和字母。
$c64 = str_split(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
);
我试着这样做
array_multisort($l64, SORT_DESC);
但它返回特殊字符、数字和字母
这种事情可以以非常“聪明”的方式实现,所以我无法抗拒:
$c64 = str_split(
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
);
$sortValue = function($char) {
return !ctype_digit($char) << 9 | !ctype_punct($char) << 8 | ord($char);
}
$sortFunction = function($x, $y) use ($sortValue) {
return $sortValue($x) - $sortValue($y);
};
usort($c64, $sortFunction);
这个怎么运作
我正在利用ord
返回字符的序数值这一事实,这既是普通sort
用于对输入进行排序的方法,也被限制在 [0, 255] 范围内——即它使用不超过 8 位价值。
这段代码所做的是获取ord
(记住:这是默认值的sort
工作方式)的返回值,并用两个额外的信息来增加它:MSB 表示“这个字符不是数字吗?” LSB 表示“这个字符不是标点符号吗?”。
这些位与序数值进行或运算,产生如下所示的 10 位量:
Bit# 10 9 8 7 6 5 4 3 2 1 0
^ ^ ^ ^
| | \-----------+-----------/
| | \------------- ord
| \---------------------------- "not punctuation" bit
\------------------------------- "not digit" bit
当您将这些值视为整数时会发生什么?显然,对应于非数字的值将大于其他任何值,类似地,对应于非标点符号的值将大于字母等。
因此,通过使用 的结果$x - $y
来确定哪个项目更大,我们实际上使数字被认为比其他所有内容都小,标点符号大于数字但小于非数字。这使得sort
先放置数字,然后是标点符号,然后是升序排序时的所有其他内容。
最后,参与比较的值是非常重要的ord
:对于同一类中的元素(例如数字),我们希望它们的排序顺序与普通sort
生成的顺序相同。
usort($c64, function($a, $b){
$aord = (is_numeric($a)) ? $a : ord($a);
$bord = (is_numeric($b)) ? $b : ord($b);
if ($aord == $bord) return 0;
return ($aord < $bord) ? -1 : 1;
});