【问题标题】:Get the second string of the URI with Perl regex使用 Perl 正则表达式获取 URI 的第二个字符串
【发布时间】:2019-10-29 05:07:27
【问题描述】:

我需要获取URI的第二部分,可能的URI是:

/api/application/v1/method
/web/application/v1/method

我可以通过以下方式获得"application"

([^\/api]\w*)

([^\/web]\w*)

但我知道这不是最好的方法,什么是好方法?

谢谢!

编辑:谢谢大家的输入,我们的目标是使用重写规则将 uri 的第二部分设置为 apache 中的标头

【问题讨论】:

  • 你用 Perl 标记了这个,但这是 Perl 代码吗?因为在 Perl 中,最好的方法是使用模块,而不是正则表达式。
  • 试试/(?:api|web)/(\w+)/v1/method

标签: regex apache perl substring uri


【解决方案1】:

一般的正则表达式(Perl 或 PCRE 语法)解决方案是:

^/[^/]+/([^/]+)

每个部分都以/ 分隔,因此只需捕获尽可能多的非/ 字符即可。

这比非贪婪的正则表达式更可取,因为它不需要回溯,并且允许部分可能包含的任何其他内容,这些部分很容易包含非单词字符,例如 - 不会被 @ 匹配987654325@.

【讨论】:

  • 虽然这个问题只是关于一个通用路径,但我认为仍然值得评论的是,这个解决方案不解析 URI(因为他们在标题中提到了它),而只是一个带有正向的路径斜线
  • 它不解析任何 URI:它解析 URI 的路径组件,就像 OP 中的示例一样。
【解决方案2】:

我们可以这样做的选项太多了,不确定哪个最好,但它可以很简单:

\/(.+?)\/(.+?)\/.*

我们想要的输出在第二个捕获组$2

Demo 1

示例

#!/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, '$2', $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 建议:

一种合法的方法,注意:

(1) 不需要尾随 .*

(2) 需要 /|$(不仅仅是 /),以防路径结束时没有 /(到 在字符串末尾终止非贪婪模式,如果没有 /)

(3) 请注意,尽管 /ee 可能容易受到攻击(即使只是出错), 因为如果第一次评估,第二次评估(e)将运行代码 结果是代码。并且可能很难确保始终如此 在完全控制下完成。更重要的是,为此目的有 没有理由进行替换——只要匹配和捕获就足够了。

【讨论】:

    【解决方案3】:

    对于所有明确要求的正则表达式,我想提出其他方法。

    这些也只解析(URI 样式)路径,就像正则表达式一样,并返回第二个目录。

    • 最基本最高效的,就是split/上的字符串

      my $dir = ( split /\//, $path )[2];
      

      split 首先返回''(在第一个/ 之前),因此我们需要第三个元素。 (请注意,我们可以为分隔符模式使用备用分隔符,它是正则表达式:split m{/}, $path。)

    • 使用适当的模块,例如URI

      use URI;
      my $dir = ( URI->new($path)->path_segments )[2];
      

      Mojo::Path

      use Mojo::Path;
      my $dir = Mojo::Path->new($path)->parts->[1];
      

    使用什么取决于你所做的细节——如果你有任何其他与 URL 和 web 相关的工作,那么你显然需要模块;否则他们可能(或可能不会)是矫枉过正。

    我已经对这些进行了基准测试,以全面检查使用模块支付的费用。

    split 要么胜过正则表达式 10-15%(使用否定字符类的正则表达式和基于非贪婪的 .+? 的正则表达式差不多),要么与它们大致相同。它们比Mojo 快大约30%,只有URI 严重滞后,比Mojo 落后5 倍。

    这适用于真实 URL 的典型路径,其中包含一些简短的组件。只有两个非常长的字符串(10k 个字符),Mojo::Path(令我惊讶的是)比split(!)高出六倍,这比字符类正则表达式领先一个数量级以上。

    如此长的字符串的否定字符类正则表达式比非贪婪的 (.+?) 高出 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 秒)

    评价 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% 非贪婪 1098243/s 648% 32% -- -5% -16% neg_cc 1158137/s 689% 39% 5% -- -11% just_split 1308227/s 792% 57% 19% 13% --

    应该记住,对于这样一个简单的工作,函数调用的开销非常大,尽管Benchmark 的工作很出色,但这些数字可能最好作为粗略的指导。

    【讨论】:

    • 这是一个网址; File::Spec 不合适,但 URI 和 Mojo::URL 合适。
    • @Grinnz 不,除了删除“URI”这个词之外,他们没有给我们任何提示,没有模式;他们要求正则表达式和所有答案,包括您的答案,只需解析路径(正确,因为这就是给出的全部)。顺便提一句。我在URI 中没有看到合适的方法(无论如何这将是一种严重的矫枉过正);与 Mojo 相同。我专门出去寻找一些东西,以便我可以支持使用模块,并最终得到了普通的 ol'File::Spec(有点令人失望)。
    • @Grinnz 如果您知道一个模块可以在 URI 的上下文中方便地解析“主机”路径,我很乐意添加它。 (或者如果我错过了URI 的某些内容)
    • @Grinnz 我很好奇,你注意到我回答中的最后一句话(来自主要部分)吗?好吧,前两个真的 - 我说这只是解析路径,不是吗?
    • 我的回答专门针对 OP 明确询问的 URL 路径。文件系统路径需要 File::Spec 提供的可移植性,例如(最常见的)允许反斜杠作为分隔符并允许在 Windows 上使用卷标。
    【解决方案4】:

    您的模式 ([^\/api]\w*) 由一个捕获组和一个否定字符类组成,它们将首先匹配 1 次而不是 /api。见demo

    在 0+ 次之后,将匹配一个单词 char。例如,该模式只能匹配未在字符类中列出的单个字符。

    你可以做的是使用一个捕获组并匹配\w+

    ^/(?:api|web)/(\w+)/v1/method
    

    说明

    • ^ 字符串开始
    • (?:api|web) 具有交替的非捕获组。匹配 api 或 web
    • (\w+)捕获组1,匹配1+字字符
    • /v1/method 与示例数据中的字面匹配。

    Regex demo

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-06
      • 2011-03-29
      相关资源
      最近更新 更多