PHP 要快速分割固定格式的文字,可以使用 split、explode 等等,但是若是一串連續的字串,該怎麼做呢?
固定字串例如:20171110235959 (2017年11月10日23點59分59秒)
一般想到最快的就是 preg_match 一行解決,再不然就是 substr,或者直接用陣列存取 $str[0] ~ $str[3] .. 等等,有沒有簡單又快速的解法呢?
PHP 依照「位置」來快速分割固定格式文字
有固定的格式的字串,要依照格式切割,可以考慮使用 sscanf 來做輸入拆解。
- 先說結論:sscanf($str, $format, $mixed...) 參數直接於後面吃進來最推薦
sscanf 的範例程式
<?php $str = '20171110235959'; // 陣列接收 $d = sscanf($str, '%4d%2d%2d%2d%2d%2d'); print_r($d); // 使用 list() 接收 list($y, $m, $d, $h, $i, $s) = sscanf($str, '%4d%2d%2d%2d%2d%2d'); // 直接於 sscanf() 後面接收 sscanf($str, '%4d%2d%2d%2d%2d%2d', $y, $m, $d, $h, $i, $s); // 同上 list() 一樣結果 ?>
preg_match、substr 的範例程式
<?php $str = '20171110235959'; // preg_match preg_match('/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/', $str, $m); print_r($m); // substr $y = substr($str, 0, 4) . "\n"; $m = substr($str, 4, 2) . "\n"; $d = substr($str, 6, 2) . "\n"; $h = substr($str, 8, 2) . "\n"; $i = substr($str, 10, 2) . "\n"; $s = substr($str, 12, 2) . "\n"; // string $y = $str[0] . $str[1] . $str[2] . $str[3]; $m = $str[4] . $str[5]; $d = $str[6] . $str[7]; $h = $str[8] . $str[9]; $i = $str[10] . $str[11]; $s = $str[12] . $str[13]; ?>
於 PHP 7.0.19 測試 preg_match、substr、sscanf、string 的速度差異
- preg_match:0.11992454528809 秒
- substr:0.026941299438477 秒
- sscanf:0.021934509277344 秒 (註:sscanf 使用 $d 陣列接收)
- sscanf:0.010013580322266 秒 (註:sscanf 使用 list() 接收)
- sscanf:0.0059604644775391 秒 (註:sscanf 直接在 function 後面接收)
- string:0.0050067901611328 秒
雖然 sscanf 沒有比字串陣列存取快,但是寫法簡單易懂,速度也比 substr、regex 的快速(跟 string 也沒差多少),可以參考使用。
註:sscanf 用來 parse access.log 應該也不錯用,於 PHP 官網看到下述範例:
<?php $line = '123.123.123.123 - - [09/Nov/2017:06:25:10 +0800] "GET /2016/01/linux-find-file-directory-perm-2016/ HTTP/1.1" 200 39239 "-" "Mozilla/5.0 (compatible)"'; $log = []; $n = sscanf(trim($line), '%s %s %s [%[^]]] "%s %s %[^"]" %d %s "%[^"]" "%[^"]"', $log['ip'], $log['client'], $log['user'], $log['time'], $log['method'], $log['uri'], $log['prot'], $log['code'], $log['bytes'], $log['ref'], $log['agent'] ); ?>