[ 这个答案是 Perl 特定的。其中的信息可能不适用于 PCRE 或其他标记语言使用的引擎。 ]
/\w/aa(实际相当于/[a-zA-Z0-9_]/)通常更快,但并非总是如此。也就是说,差异是如此之小(每次检查不到 1 纳秒),这不应该是一个问题。把它放在上下文中,调用 sub 或启动正则表达式引擎需要更长的时间。
接下来会详细介绍这一点。
首先,\w 默认情况下与[a-zA-Z0-9_] 不同。 \w 匹配每个
字母、数字、标记和连接符标点符号 Unicode 代码点。其中有 119,821 个![1] 确定哪个是最快的非等效代码是没有意义的。
但是,将\w 与/aa 一起使用可确保\w 仅匹配[a-zA-Z0-9_]。这就是我们将用于基准测试的内容。 (实际上,我们会同时使用两者。)
(请注意,每个测试执行 1000 万次检查,因此 10.0/s 的速率实际上意味着每秒 1000 万次检查。)
ASCII-only positive match
Rate [a-zA-Z0-9_] (?u:\w) (?aa:\w)
[a-zA-Z0-9_] 39.1/s -- -26% -36%
(?u:\w) 52.9/s 35% -- -13%
(?aa:\w) 60.9/s 56% 15% --
在 ASCII 字符中查找匹配项时,仅 ASCII \w 和 Unicode \w 均优于显式类。
/\w/aa 在我的机器上是 (1/39.1 - 1/60.9) / 10,000,000 = 0.000,000,000,916 秒
ASCII-only negative match
Rate (?u:\w) (?aa:\w) [a-zA-Z0-9_]
(?u:\w) 27.2/s -- -0% -12%
(?aa:\w) 27.2/s 0% -- -12%
[a-zA-Z0-9_] 31.1/s 14% 14% --
当在 ASCII 字符中找不到匹配项时,显式类优于纯 ASCII \w。
/[a-zA-Z0-9_]/ 在我的机器上是 (1/27.2 - 1/31.1) / 10,000,000 = 0.000,000,000,461 秒
Non-ASCII positive match
Rate (?u:\w) [a-zA-Z0-9_] (?aa:\w)
(?u:\w) 2.97/s -- -100% -100%
[a-zA-Z0-9_] 3349/s 112641% -- -9%
(?aa:\w) 3664/s 123268% 9% --
哇哦。此测试似乎正在运行一些优化。也就是说,多次运行测试会产生非常一致的结果。 (其他测试也是如此。)
在非 ASCII 字符中查找匹配项时,仅 ASCII 的 \w 优于显式类。
/\w/aa 在我的机器上是 (1/3349 - 1/3664) / 10,000,000 = 0.000,000,000,002,57 秒
Non-ASCII negative match
Rate (?u:\w) [a-zA-Z0-9_] (?aa:\w)
(?u:\w) 2.66/s -- -9% -71%
[a-zA-Z0-9_] 2.91/s 10% -- -68%
(?aa:\w) 9.09/s 242% 212% --
当无法在非 ASCII 字符中找到匹配项时,仅 ASCII 的 \w 优于显式类。
/[a-zA-Z0-9_]/ 在我的机器上是 (1/2.91 - 1/9.09) / 10,000,000 = 0.000,000,002,34 秒
结论
- 我很惊讶
/\w/aa 和 /[a-zA-Z0-9_]/ 之间有任何区别。
- 在某些情况下,
/\w/aa 更快;在其他人中,/[a-zA-Z0-9_]/。
-
/\w/aa 和 /[a-zA-Z0-9_]/ 之间的差异非常小(小于 1 纳秒)。
- 差别很小,您不必担心。
- 即使
/\w/aa 和/\w/u 之间的差异也很小,尽管后者匹配的字符数比前者多 4 个数量级。
use strict;
use warnings;
use feature qw( say );
use Benchmarks qw( cmpthese );
my %pos_tests = (
'(?u:\\w)' => '/^\\w*\\z/u',
'(?aa:\\w)' => '/^\\w*\\z/aa',
'[a-zA-Z0-9_]' => '/^[a-zA-Z0-9_]*\\z/',
);
my %neg_tests = (
'(?u:\\w)' => '/\\w/u',
'(?aa:\\w)' => '/\\w/aa',
'[a-zA-Z0-9_]' => '/[a-zA-Z0-9_]/',
);
$_ = sprintf( 'use strict; use warnings; our $s; for (1..1000) { $s =~ %s }', $_)
for
values(%pos_tests),
values(%neg_tests);
local our $s;
say "ASCII-only positive match";
$s = "J" x 10_000;
cmpthese(-3, \%pos_tests);
say "";
say "ASCII-only negative match";
$s = "!" x 10_000;
cmpthese(-3, \%neg_tests);
say "";
say "Non-ASCII positive match";
$s = "\N{U+0100}" x 10_000;
cmpthese(-3, \%pos_tests);
say "";
say "Non-ASCII negative match";
$s = "\N{U+2660}" x 10_000;
cmpthese(-3, \%neg_tests);
- Unicode 版本 11。