我想知道\w
通过的字符列表,是[a-zA-Z0-9_]
还是它可能涵盖的更多字符?
我问的是这个问题,因为基于this,\d
与[0-9]
不同,并且是less efficient。
\w
与[a-zA-Z0-9_]
:在大规模生产中哪个更快?
答案 0 :(得分:5)
[此答案是Perl特定的。其中的信息可能不适用于PCRE或其他已标记语言的引擎。]
/\w/aa
(实际上等效于/[a-zA-Z0-9_]/
)通常更快,但并非总是如此。就是说,差异是如此之小(每次检查少于1纳秒),所以不必担心。要将其放在上下文中,调用子程序或启动正则表达式引擎需要花费很长的时间。
下面将详细介绍这一点。
首先,默认情况下\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 s在我的计算机上更快
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 s在我的计算机上更快
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 s在我的计算机上更快
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 s在我的计算机上更快
结论
/\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);
和{{1}}之间的差异也很小。{{1}}
答案 1 :(得分:3)
此答案基于Perl,但以下所有带标签的工具应非常相似。
\w
字符类(用于“单词”字符)遵循Unicode规范,用于“单词”的字符属性。这包括太多的东西和复杂性,以至于很难指定包含属性的类别。请参见perlrecharclass中的“ 文字字符”,例如,请参见this post。有关背景,请参见perlunicode和perluniprops。
简而言之,除非使用/a
(或/aa
)modifier或语言环境,否则它超出了63个ascii字符。
但是,问题特别是关于性能。此时,应该期望不同的工具在行为上可能有所不同,并且可能会有很多不同,因为这取决于正则表达式的实现。这篇文章的其余部分专门针对Perl。
人们可能希望检查较小的集合可能更快,或者可能期望\w
之类的结构具有优化功能。让我们来衡量,而不是猜测。以下是一个旨在寻找合理结果的粗略基准,省略了一些细微差别。
use warnings;
use strict;
use feature 'say';
use List::Util qw(shuffle);
use Benchmark qw(cmpthese);
my $run_for = shift // 3; # seconds to run benchmark for
my $str = join '', (shuffle 'a'..'z', 'A'..'Z', 0..9, '_') x 100;
sub word_class {
my $str = shift;
my @m_1 = $str =~ /\w/g;
return \@m_1;
}
sub char_class {
my $str = shift;
my @m_2 = $str =~ /[a-zA-Z0-9_]/g;
return \@m_2;
}
cmpthese(-$run_for, {
word => sub { my $res = word_class ($str) },
char => sub { my $res = char_class ($str) },
});
使用[a-zA-Z0-9_]
组装一个字符串,将其重新排列,然后重复100次。整个字符串在/g
下,\w
和[a-zA-Z0-9_]
下被逐字匹配。因此,在每种情况下都是一个正则表达式,并且已经过基准测试。
结果
Rate char word char 583/s -- -1% word 587/s 1% --
在我的测试中,上述数字在任何一次运行中都高达2%。所以没有区别。
注意:我尝试将非ASCII字符添加到测试字符串中,没有明显区别。
注意:带有/g
的正则表达式在char后面累积char(6300)匹配项,但是在单个引擎运行中。另一种选择是重复检查单个匹配项。它们并不相同,但是无论两者是否都会在\w
和[a-zA-Z0-9_]
之间暴露出相当大的性能差异。
请为自己计时,并使用更适合您情况的字符串和样式。
以上基准旨在作为一项基本的粗略衡量标准。但是,负(失败)匹配项尤其明显地丢失了,因此,预计引擎将通过所有种测试模式的可能性。
我通过在目标字符串更改为上面调用基准测试例程来测试这一点
$str = join '', qw(! / \ { } ^ % @) x 1_000;
在\w
和[a-zA-Z0-9_]
下都将不匹配。结果
Rate char word char 72820/s -- -19% word 89863/s 23% --至少可以说,这令我感到惊讶。
\w
集要大得多(请参见池上的答案),这必然意味着正在进行大量(或“神奇”)优化。
这证明了我的总体结论:总体而言,它们的性能足够接近,因此只需使用更合适的编码方式即可;或者,根据您的特定用例对其进行计时。
答案 2 :(得分:1)
\w
应该取决于语言环境的设置;
LANG=
LC_CTYPE=
LC_ALL=
如果我的话那么真实,那么\w
不仅应该是[A-Za-z_],还应该是许多其他UCS字符,
如果将其设置为LANG=en_US
,Imho只是[A-Za-z_],
参见Explain the effects of export LANG, LC_CTYPE, LC_ALL
\d
可以是原样,或者[0-9]
取决于正则表达式引擎,
sed's \d
甚至不能通过其[0-9]
选项来-E
,只有更好的正则表达式引擎才是,而由[0-9]
表示的gnu
用{{1 }}
恕我直言,为类集预设的所有正则表达式速记都比正常的[[:digit:]]
类集要快
[]
比\w, \d
快
[A-Za-z_], [0-9]
比[^ A-Za-z_]快,等等。