使用 Perl 正则表达式获取 URI 的第二个字符串
Get the second string of the URI with Perl regex
我需要获取URI的第二部分,可能的URI是:
/api/application/v1/method
/web/application/v1/method
我可以 "application"
使用:
([^\/api]\w*)
和
([^\/web]\w*)
但我知道这不是最好的方法,什么才是好的方法?
谢谢!
编辑:感谢大家的投入,目标是将 uri 的第二部分设置为具有重写规则的 apache header
一般的正则表达式(Perl 或 PCRE 语法)解决方案是:
^/[^/]+/([^/]+)
每个部分都用 /
分隔,所以只要捕获尽可能多的非 /
字符即可。
这比 non-greedy 正则表达式更可取,因为它不需要回溯,并且允许这些部分可能包含的任何其他内容,这些内容可以轻松包含 non-word 个字符,例如 -
\w
.
不匹配
您的模式 ([^\/api]\w*)
由一个捕获组和一个否定字符 class 组成,它将首先匹配 1 次而不是 /
、a
、p
或 i
。参见 demo。
之后 0+ 次一个单词 char 将被匹配。例如,该模式可以只匹配字符 class.
中未列出的单个字符
您可能会做的是使用捕获组并匹配 \w+
^/(?:api|web)/(\w+)/v1/method
说明
^
字符串开头
(?:api|web)
非捕获组交替。匹配 api 或 web
(\w+)
捕获第 1 组,匹配 1+ 个单词字符
/v1/method
与您的示例数据中的字面匹配。
我们可以这样做的选项太多了,不确定哪一个最好,但它可以像这样简单:
\/(.+?)\/(.+?)\/.*
我们想要的输出在第二个捕获组中 </code>。</p>
<h3><a href="https://regex101.com/r/2T7kZA/1/" rel="nofollow noreferrer">Demo 1</a></h3>
<h3>例子</h3>
<pre><code>#!/usr/bin/perl -w
use strict;
use warnings;
use feature qw( say );
main();
sub main{
my $string = '/api/application/v1/method
/web/application/v1/method';
my $pattern = '\/(.+?)\/(.+?)\/.*';
my $match = replace($pattern, '', $string);
say $match , " is a match ";
}
sub replace {
my ($pattern, $replacement, $string) = @_;
$string =~s/$pattern/$replacement/gee;
return $string;
}
输出
application
application is a match
建议
zdim 建议:
A legitimate approach, notes:
(1) there is no need for the trailing .*
(2) Need /|$ (not just /), in case the path finishes without / (to
terminate the non-greedy pattern at the end of string, if there is no
/)
(3) note though that /ee can be vulnerable (even just to errors),
since the second evaluation (e) will run code if the first evaluation
results in code. And it may be difficult to ensure that that is always
done under full control. More to the point, for this purpose there is
no reason to run a substitution --- just match and capture is enough.
有了明确要求的所有正则表达式,我想提出其他方法。
这些也只解析一个(URI 样式)路径,就像正则表达式一样,return 第二个目录。
最基本高效的,就是split/
上的字符串
my $dir = ( split /\//, $path )[2];
split
returns ''
第一(在第一个 /
之前)因此我们需要第三个元素。 (请注意,我们 可以 为分隔符模式使用备用分隔符,它是正则表达式:split m{/}, $path
。)
使用合适的模块,例如URI
use URI;
my $dir = ( URI->new($path)->path_segments )[2];
use Mojo::Path;
my $dir = Mojo::Path->new($path)->parts->[1];
使用什么取决于您所做工作的细节——如果您有任何其他使用 URL 和网络的工作,那么您显然需要相应的模块;否则他们可能(或可能不会)矫枉过正。
我已经对这些进行了基准测试,以检查人们使用模块支付的费用。
split
要么比正则表达式快 10-15%
(使用否定字符 class 的正则表达式和基于 non-greedy .+?
的正则表达式出现相同)或与它们大致相同。它们比 Mojo
快大约 30%
,只有 URI
严重落后,落后 Mojo
.
5 倍
这是 real-life URL 的典型路径,包含一些短组件。只有两个非常长的字符串(10k 个字符),Mojo::Path
(对我来说令人惊讶)比 split
(!)领先六倍,比 character-class 正则表达式领先超过一个数量级。
如此长的字符串的取反-character-class 正则表达式比 non-greedy (.+?
) 高出 3 倍,很高兴知道它本身。
在这一切中,URI 和 Mojo 对象都是提前创建的。
基准代码。我想指出,这些时间安排的细节远不如代码的结构和质量重要。
use warnings;
use strict;
use feature 'say';
use URI;
use Mojo::Path;
use Benchmark qw(cmpthese);
my $runfor = shift // 3; #/
#my $path = '/' . 'a' x 10_000 . '/' . 'X' x 10_000;
my $path = q(/api/app/v1/method);
my $uri = URI->new($path);
my $mojo = Mojo::Path->new($path);
sub neg_cc {
my ($dir) = $path =~ m{ [^/]+ / ([^/]+) }x; return $dir; #/
}
sub non_greedy {
my ($dir) = $path =~ m{ .+? / (.+?) (?:/|$) }x; return $dir; #/
}
sub URI_path {
my $dir = ( $uri->path_segments )[2]; return $dir;
}
sub Mojo_path {
my $dir = $mojo->parts->[1]; return $dir;
}
sub just_split {
my $dir = ( split /\//, $path )[2]; return $dir;
}
cmpthese( -$runfor, {
neg_cc => sub { neg_cc($path) },
non_greedy => sub { non_greedy($path) },
just_split => sub { just_split($path) },
URI_path => sub { URI_path($path) },
Mojo_path => sub { Mojo_path($path) },
});
在装有 v5.16
的笔记本电脑上打印(10 秒)运行
Rate URI_path Mojo_path non_greedy neg_cc just_split
URI_path 146731/s -- -82% -87% -87% -89%
Mojo_path 834297/s 469% -- -24% -28% -36%
non_greedy 1098243/s 648% 32% -- -5% -16%
neg_cc 1158137/s 689% 39% 5% -- -11%
just_split 1308227/s 792% 57% 19% 13% --
应该记住,function-call 的开销对于这样一个简单的工作来说是非常大的,尽管 Benchmark
的工作,这些数字可能最好作为粗略的指南.
我需要获取URI的第二部分,可能的URI是:
/api/application/v1/method
/web/application/v1/method
我可以 "application"
使用:
([^\/api]\w*)
和
([^\/web]\w*)
但我知道这不是最好的方法,什么才是好的方法?
谢谢!
编辑:感谢大家的投入,目标是将 uri 的第二部分设置为具有重写规则的 apache header
一般的正则表达式(Perl 或 PCRE 语法)解决方案是:
^/[^/]+/([^/]+)
每个部分都用 /
分隔,所以只要捕获尽可能多的非 /
字符即可。
这比 non-greedy 正则表达式更可取,因为它不需要回溯,并且允许这些部分可能包含的任何其他内容,这些内容可以轻松包含 non-word 个字符,例如 -
\w
.
您的模式 ([^\/api]\w*)
由一个捕获组和一个否定字符 class 组成,它将首先匹配 1 次而不是 /
、a
、p
或 i
。参见 demo。
之后 0+ 次一个单词 char 将被匹配。例如,该模式可以只匹配字符 class.
中未列出的单个字符您可能会做的是使用捕获组并匹配 \w+
^/(?:api|web)/(\w+)/v1/method
说明
^
字符串开头(?:api|web)
非捕获组交替。匹配 api 或 web(\w+)
捕获第 1 组,匹配 1+ 个单词字符/v1/method
与您的示例数据中的字面匹配。
我们可以这样做的选项太多了,不确定哪一个最好,但它可以像这样简单:
\/(.+?)\/(.+?)\/.*
我们想要的输出在第二个捕获组中 </code>。</p>
<h3><a href="https://regex101.com/r/2T7kZA/1/" rel="nofollow noreferrer">Demo 1</a></h3>
<h3>例子</h3>
<pre><code>#!/usr/bin/perl -w
use strict;
use warnings;
use feature qw( say );
main();
sub main{
my $string = '/api/application/v1/method
/web/application/v1/method';
my $pattern = '\/(.+?)\/(.+?)\/.*';
my $match = replace($pattern, '', $string);
say $match , " is a match ";
}
sub replace {
my ($pattern, $replacement, $string) = @_;
$string =~s/$pattern/$replacement/gee;
return $string;
}
输出
application
application is a match
建议
zdim 建议:
A legitimate approach, notes:
(1) there is no need for the trailing .*
(2) Need /|$ (not just /), in case the path finishes without / (to terminate the non-greedy pattern at the end of string, if there is no /)
(3) note though that /ee can be vulnerable (even just to errors), since the second evaluation (e) will run code if the first evaluation results in code. And it may be difficult to ensure that that is always done under full control. More to the point, for this purpose there is no reason to run a substitution --- just match and capture is enough.
有了明确要求的所有正则表达式,我想提出其他方法。
这些也只解析一个(URI 样式)路径,就像正则表达式一样,return 第二个目录。
最基本高效的,就是split
上的字符串/
my $dir = ( split /\//, $path )[2];
split
returns''
第一(在第一个/
之前)因此我们需要第三个元素。 (请注意,我们 可以 为分隔符模式使用备用分隔符,它是正则表达式:split m{/}, $path
。)使用合适的模块,例如URI
use URI; my $dir = ( URI->new($path)->path_segments )[2];
use Mojo::Path; my $dir = Mojo::Path->new($path)->parts->[1];
使用什么取决于您所做工作的细节——如果您有任何其他使用 URL 和网络的工作,那么您显然需要相应的模块;否则他们可能(或可能不会)矫枉过正。
我已经对这些进行了基准测试,以检查人们使用模块支付的费用。
split
要么比正则表达式快 10-15%
(使用否定字符 class 的正则表达式和基于 non-greedy .+?
的正则表达式出现相同)或与它们大致相同。它们比 Mojo
快大约 30%
,只有 URI
严重落后,落后 Mojo
.
这是 real-life URL 的典型路径,包含一些短组件。只有两个非常长的字符串(10k 个字符),Mojo::Path
(对我来说令人惊讶)比 split
(!)领先六倍,比 character-class 正则表达式领先超过一个数量级。
如此长的字符串的取反-character-class 正则表达式比 non-greedy (.+?
) 高出 3 倍,很高兴知道它本身。
在这一切中,URI 和 Mojo 对象都是提前创建的。
基准代码。我想指出,这些时间安排的细节远不如代码的结构和质量重要。
use warnings;
use strict;
use feature 'say';
use URI;
use Mojo::Path;
use Benchmark qw(cmpthese);
my $runfor = shift // 3; #/
#my $path = '/' . 'a' x 10_000 . '/' . 'X' x 10_000;
my $path = q(/api/app/v1/method);
my $uri = URI->new($path);
my $mojo = Mojo::Path->new($path);
sub neg_cc {
my ($dir) = $path =~ m{ [^/]+ / ([^/]+) }x; return $dir; #/
}
sub non_greedy {
my ($dir) = $path =~ m{ .+? / (.+?) (?:/|$) }x; return $dir; #/
}
sub URI_path {
my $dir = ( $uri->path_segments )[2]; return $dir;
}
sub Mojo_path {
my $dir = $mojo->parts->[1]; return $dir;
}
sub just_split {
my $dir = ( split /\//, $path )[2]; return $dir;
}
cmpthese( -$runfor, {
neg_cc => sub { neg_cc($path) },
non_greedy => sub { non_greedy($path) },
just_split => sub { just_split($path) },
URI_path => sub { URI_path($path) },
Mojo_path => sub { Mojo_path($path) },
});
在装有 v5.16
的笔记本电脑上打印(10 秒)运行Rate URI_path Mojo_path non_greedy neg_cc just_split URI_path 146731/s -- -82% -87% -87% -89% Mojo_path 834297/s 469% -- -24% -28% -36% non_greedy 1098243/s 648% 32% -- -5% -16% neg_cc 1158137/s 689% 39% 5% -- -11% just_split 1308227/s 792% 57% 19% 13% --
应该记住,function-call 的开销对于这样一个简单的工作来说是非常大的,尽管 Benchmark
的工作,这些数字可能最好作为粗略的指南.