Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/perl/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Regex 带可变部分的正则表达式_Regex_Perl - Fatal编程技术网

Regex 带可变部分的正则表达式

Regex 带可变部分的正则表达式,regex,perl,Regex,Perl,如何将这两个正则表达式合并为一个正则表达式,该正则表达式根据字符串结构捕获所有可用部分($s中的最后3个字段是可选的,如果存在,则应捕获)?使用(?=…)我无法得到有效的解决方案 $s='1.2.3.4 - egon [10/Dec/2007:21:07:20 +0100] "GET /x.htm HTTP/1.1" 401 488'; $re = qr/\A (\d+)\.(\d+)\.(\d+)\.(\d+) [ ] (\S+) [ ] (\S+)

如何将这两个正则表达式合并为一个正则表达式,该正则表达式根据字符串结构捕获所有可用部分($s中的最后3个字段是可选的,如果存在,则应捕获)?使用(?=…)我无法得到有效的解决方案

$s='1.2.3.4 - egon  [10/Dec/2007:21:07:20 +0100] "GET /x.htm HTTP/1.1" 401 488';
$re = qr/\A
        (\d+)\.(\d+)\.(\d+)\.(\d+)
    [ ] (\S+)
    [ ] (\S+)
    [ ]+ \[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+) [ ] (\S+)\]
    [ ] "(\S+) [ ] (.*?) [ ] (\S+)"
    [ ] (\S+)
    [ ] (\S+)
    \Z/x;
print "[".join('],[',$s =~ $re)."]\n\n";   

$s='1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283 "-" "Mozilla/5.0..." "-"';
$re = qr/\A
        (\d+)\.(\d+)\.(\d+)\.(\d+)
    [ ] (\S+)
    [ ] (\S+)
    [ ]+ \[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+) [ ] (\S+)\]
    [ ] "(\S+) [ ] (.*?) [ ] (\S+)"
    [ ] (\S+)
    [ ] (\S+) [ ] "(.*?)" [ ] "(.*?)" [ ] "(.*?)"
        \Z
        /x;
print "[".join('],[',$s =~ $re)."]\n\n";   

您可以使用非捕获组
(?:)
,而不是使用前瞻
(?=)
,并匹配零次或一次出现:

$re = qr/\A
        (\d+)\.(\d+)\.(\d+)\.(\d+)
    [ ] (\S+)
    [ ] (\S+)
    [ ]+ \[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+) [ ] (\S+)\]
    [ ] "(\S+) [ ] (.*?) [ ] (\S+)"
    [ ] (\S+)
    [ ] (\S+)
    (?:
        [ ] "(.*?)"
        [ ] "(.*?)"
        [ ] "(.*?)"
    )?
    \Z/x;
这将产生固定长度的捕获数组,但如果可选捕获组不匹配,则最后3个将是未定义的。如果必须在1到3个可选字段之间进行匹配,请在其各自的非捕获组中使用零或更多(
)次将每个字段包装起来。我也试过这个,但不起作用:

(?: [ ] "(.*?)" ){0,3} \Z
它匹配并捕获最后三个字段中的每一个,但每次捕获都会覆盖捕获数组中的最终位置,因此捕获完成后,它只包含最终字段


我要提醒您,您使用的是一个可能不适用于所有web日志的非常严格的表达式:具体来说,IP地址的匹配不会处理IPv6地址,而用户代理的匹配可能不会处理带有
字符的用户代理,这取决于它们的转义方式(例如,lighttpd 1.4.28不会转义它们)。

您可以使用非捕获组
(?:)
,而不是使用前瞻
(?=)
,并匹配零或一个事件:

$re = qr/\A
        (\d+)\.(\d+)\.(\d+)\.(\d+)
    [ ] (\S+)
    [ ] (\S+)
    [ ]+ \[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+) [ ] (\S+)\]
    [ ] "(\S+) [ ] (.*?) [ ] (\S+)"
    [ ] (\S+)
    [ ] (\S+)
    (?:
        [ ] "(.*?)"
        [ ] "(.*?)"
        [ ] "(.*?)"
    )?
    \Z/x;
这将产生固定长度的捕获数组,但如果可选捕获组不匹配,则最后3个将是未定义的。如果必须在1和3个可选字段之间进行匹配,请在其各自的非捕获组中使用零或更多(
)次将每个字段包装起来。我也尝试了此操作,但无效:

(?: [ ] "(.*?)" ){0,3} \Z
它匹配并捕获最后三个字段中的每一个,但每次捕获都会覆盖捕获数组中的最终位置,因此捕获完成后,它只包含最终字段


我要提醒您,您使用的是一个可能不适用于所有web日志的非常严格的表达式:具体来说,IP地址的匹配不会处理IPv6地址,而用户代理的匹配可能不会处理带有
字符的用户代理,这取决于它们的转义方式(例如,lighttpd 1.4.28并没有逃逸它们)。

当您的正则表达式看起来像这样时,我认为开始考虑替代方案是一个好主意。在这种情况下,您可以尝试,因为您的字符串有点分隔,并且包含带引号的字符串。它是perl 5中的核心模块

基本上,我们所做的是为我们期望的分隔符提供一个正则表达式,为保留引号提供一个0或1,以及输入行本身

use strict;
use warnings;
use Text::ParseWords;

my $s = '1.2.3.4 - egon  [10/Dec/2007:21:07:20 +0100] "GET /x.htm HTTP/1.1" 401 488';
my @s = quotewords('[\s/:\[\].]+', 0, $s);
print "[".join('],[',@s)."]\n\n";   

$s = '1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283 "-" "Mozilla/5.0..." "-"';
@s = quotewords('[\s/:\[\].]+', 0, $s);
print "[".join('],[',@s)."]\n\n";   
输出:

[1],[2],[3],[4],[-],[egon],[10],[Dec],[2007],[21],[07],[20],[+0100],[GET /x.htm
HTTP/1.1],[401],[488]

[1],[2],[3],[4],[-],[-],[13],[Jun],[2007],[01],[37],[44],[+0200],[GET /x.htm HTT
P/1.0],[404],[283],[-],[Mozilla/5.0...],[-]

当您的正则表达式开始看起来像这样时,我认为开始考虑替代方案是个好主意。在本例中,您可以尝试,因为您的字符串有点分隔并包含带引号的字符串。它是perl 5中的核心模块

基本上,我们所做的是为我们期望的分隔符提供一个正则表达式,为保留引号提供一个0或1,以及输入行本身

use strict;
use warnings;
use Text::ParseWords;

my $s = '1.2.3.4 - egon  [10/Dec/2007:21:07:20 +0100] "GET /x.htm HTTP/1.1" 401 488';
my @s = quotewords('[\s/:\[\].]+', 0, $s);
print "[".join('],[',@s)."]\n\n";   

$s = '1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283 "-" "Mozilla/5.0..." "-"';
@s = quotewords('[\s/:\[\].]+', 0, $s);
print "[".join('],[',@s)."]\n\n";   
输出:

[1],[2],[3],[4],[-],[egon],[10],[Dec],[2007],[21],[07],[20],[+0100],[GET /x.htm
HTTP/1.1],[401],[488]

[1],[2],[3],[4],[-],[-],[13],[Jun],[2007],[01],[37],[44],[+0200],[GET /x.htm HTT
P/1.0],[404],[283],[-],[Mozilla/5.0...],[-]

我不想谈任何解决办法

我以前是怎么说的:好主意。 但它只做包名谓词所做的事情:ParseWords

“如果您想继续讨论,请为我找到一个测试用例,其中您的正则表达式工作,而我的解决方案失败…”

当然,出于我的目的,我已经测试了您的解决方案

在您的解决方案中,根据输入,字段会四处移动

使用正则表达式,我将发现字段始终位于定义的位置

(例如:Authuser位于$token[5],Year位于$token[9])

以下是测试:

#!/usr/bin/perl -w
use strict;
use warnings;
use FileHandle;
use Text::ParseWords;

my $re = qr/\A
        (\d+)\.(\d+)\.(\d+)\.(\d+)
    [ ] (\S+)
    (?: [ ] (\S*))? (?: [ ] (\S*))?
    [ ] \[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+) [ ] (\S+)\]
    [ ] "(?:(\S+) [ ])? (.*?) (?:[ ] (\S+))?"
    [ ] (\S+)
    [ ] (\S+)
    (?:
        [ ] "(.*?)"
        [ ] "(.*?)"
        [ ] "(.*?)"
    )?
    \Z/x;

my (@s,@token);
#---- most entries ------------------------------------------------------------
push(@s,'1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283');
#---- referer, user agent, ... ------------------------------------------------
push(@s,'1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283 "-" "Mozilla/5.0..." "-"');
#---- auth without password ---------------------------------------------------
push(@s,'1.2.3.4 - ausr  [10/Dec/2007:21:07:20 +0100] "GET /x.htm HTTP/1.1" 401 488');
#---- no http request --------------------------------------------------------- 
push(@s,'1.2.3.4 - - [13/Jun/2007:19:16:18 +0200] "-" 408 -');
#---- auth with password ------------------------------------------------------
push(@s,'1.2.3.4 - ausr pwd [12/Jul/2006:16:55:04 +0200] "GET /x.htm HTTP/1.1" 401 489');
#---- auth without user -------------------------------------------------------
push(@s,'1.2.3.4 -  pwd [16/Aug/2007:08:43:50 +0200] "GET /x.htm HTTP/1.1" 401 489');
#---- multiple words in request -----------------------------------------------
push(@s,'1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /this is test HTTP/1.0" 404 283'); 

no warnings 'uninitialized';
foreach(@s)
{ @token=$_ =~ $re;
  print "regex:      AUTHUSER=".$token[5].", YEAR=".$token[9]."\n";
  @token=quotewords('[\s/:\[\].]+', 0, $_);
  print "quotewords: AUTHUSER=".$token[5].", YEAR=".$token[9]."\n\n";
}
结果如下:

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=01

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=01

regex:      AUTHUSER=ausr, YEAR=2007
quotewords: AUTHUSER=ausr, YEAR=21

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=19

regex:      AUTHUSER=ausr, YEAR=2006
quotewords: AUTHUSER=ausr, YEAR=2006

regex:      AUTHUSER=, YEAR=2007
quotewords: AUTHUSER=pwd, YEAR=08

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=01

我不想谈任何解决办法

我以前是怎么说的:好主意。 但它只做包名谓词所做的事情:ParseWords

“如果您想继续讨论,请为我找到一个测试用例,其中您的正则表达式工作,而我的解决方案失败…”

当然,出于我的目的,我已经测试了您的解决方案

在您的解决方案中,根据输入,字段会四处移动

使用正则表达式,我将发现字段始终位于定义的位置

(例如:Authuser位于$token[5],Year位于$token[9])

以下是测试:

#!/usr/bin/perl -w
use strict;
use warnings;
use FileHandle;
use Text::ParseWords;

my $re = qr/\A
        (\d+)\.(\d+)\.(\d+)\.(\d+)
    [ ] (\S+)
    (?: [ ] (\S*))? (?: [ ] (\S*))?
    [ ] \[(\d+)\/(\S+)\/(\d+):(\d+):(\d+):(\d+) [ ] (\S+)\]
    [ ] "(?:(\S+) [ ])? (.*?) (?:[ ] (\S+))?"
    [ ] (\S+)
    [ ] (\S+)
    (?:
        [ ] "(.*?)"
        [ ] "(.*?)"
        [ ] "(.*?)"
    )?
    \Z/x;

my (@s,@token);
#---- most entries ------------------------------------------------------------
push(@s,'1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283');
#---- referer, user agent, ... ------------------------------------------------
push(@s,'1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /x.htm HTTP/1.0" 404 283 "-" "Mozilla/5.0..." "-"');
#---- auth without password ---------------------------------------------------
push(@s,'1.2.3.4 - ausr  [10/Dec/2007:21:07:20 +0100] "GET /x.htm HTTP/1.1" 401 488');
#---- no http request --------------------------------------------------------- 
push(@s,'1.2.3.4 - - [13/Jun/2007:19:16:18 +0200] "-" 408 -');
#---- auth with password ------------------------------------------------------
push(@s,'1.2.3.4 - ausr pwd [12/Jul/2006:16:55:04 +0200] "GET /x.htm HTTP/1.1" 401 489');
#---- auth without user -------------------------------------------------------
push(@s,'1.2.3.4 -  pwd [16/Aug/2007:08:43:50 +0200] "GET /x.htm HTTP/1.1" 401 489');
#---- multiple words in request -----------------------------------------------
push(@s,'1.2.3.4 - - [13/Jun/2007:01:37:44 +0200] "GET /this is test HTTP/1.0" 404 283'); 

no warnings 'uninitialized';
foreach(@s)
{ @token=$_ =~ $re;
  print "regex:      AUTHUSER=".$token[5].", YEAR=".$token[9]."\n";
  @token=quotewords('[\s/:\[\].]+', 0, $_);
  print "quotewords: AUTHUSER=".$token[5].", YEAR=".$token[9]."\n\n";
}
结果如下:

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=01

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=01

regex:      AUTHUSER=ausr, YEAR=2007
quotewords: AUTHUSER=ausr, YEAR=21

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=19

regex:      AUTHUSER=ausr, YEAR=2006
quotewords: AUTHUSER=ausr, YEAR=2006

regex:      AUTHUSER=, YEAR=2007
quotewords: AUTHUSER=pwd, YEAR=08

regex:      AUTHUSER=-, YEAR=2007
quotewords: AUTHUSER=-, YEAR=01

谢谢你的提示。你永远不会停止学习。你的警告也很有用。我会测试ist。@Bonsaiviking谢谢你的提示。你永远不会停止学习。你的警告也很有用。我会测试ist。@Bonsaiviking这个主意不错,但不幸的是,你不能保留单词的分组。例如,请求或用户代理可能包含更多的内容e字和应作为单个标量返回@TLP@bootware我不知道你在说什么。使用此解决方案,请求或用户代理可以包含任意数量的单词,这无关紧要。另一方面,你的正则表达式只能处理3个单词,其他情况下会中断。正则表达式处理请求的所有单词n把它们放在一起-看这里:
$s='1.2.3.4-[13/Jun/2007:01:37:44+0200]“GET/this是一个测试HTTP/1.0”404 283“;
print”[”。join('],[',$s=~$re)。“]\n\n\n”
[1],[2],[3],[4],[4],[13],[6],[2007],[01],[37],[44],[0200],[GET/this是一个测试HTTP],[2830],[HTTP],[1],[1],[1],[1],[1],[1],[1],[1],[1],[1
@TLP@bootware不,正则表达式中断了
GET
HTTP/1.0
。我的解决方案保持了字符串的完整性:
GET/这是一个测试HTTP/1.0
。我的解决方案的好处是它更容易维护。如果你想对请求字符串进行后期处理并中断GET和HTTP/1.0,你可以很容易地做到。@bootware,不。如果在身份验证中没有密码,则您的正则表达式也会失败。因此…不。您不必使用此解决方案,但请不要试图编造不真实的内容。这太烦人了。请给我一个测试用例,让您的正则表达式和我的解决方案工作