Regex Perl用“替换数字”;x";在字符串中,但仅在一个特定位置
我正在解析一个充满各种错误的日志文件。这些都是网络错误,这意味着客户在格式化我们网站的日期时犯了错误。日志如下所示:Regex Perl用“替换数字”;x";在字符串中,但仅在一个特定位置,regex,perl,Regex,Perl,我正在解析一个充满各种错误的日志文件。这些都是网络错误,这意味着客户在格式化我们网站的日期时犯了错误。日志如下所示: Error 123: Customer 2: Bad Date [17/12/2014] Error 123: Customer 2: Bad Date [19/12/2014] Error 123: Customer 1: Bad Date [123/23/222] Error 123: Customer 2: Bad Date [null] Error 123: Custom
Error 123: Customer 2: Bad Date [17/12/2014]
Error 123: Customer 2: Bad Date [19/12/2014]
Error 123: Customer 1: Bad Date [123/23/222]
Error 123: Customer 2: Bad Date [null]
Error 123: Customer 6: Bad Date [12/14:]
Error 123: Customer 6: Bad Date [12/16:]
现在,对于同一个客户,前两个错误实际上是相同的。在这两行中,日期都报告为DD/MM/YYYY
,而不是YYYY/MM/DD
,因此我不需要两次报告此错误。对于同一客户,最后两行也是相同的错误。使用了MM/DD
,并省略了年份。null
日期是另一个错误,尽管我以前报告过Customer#2的坏日期错误。在某个地方,他们传递了一个空日期
我想做的是这样比较这些行:
Error 123: Customer 2: Bad Date [xx/xx/xxxx]
Error 123: Customer 2: Bad Date [xx/xx/xxxx]
Error 123: Customer 1: Bad Date [xxx/xx/xxx]
Error 123: Customer 2: Bad Date [null]
Error 123: Customer 6: Bad Date [xx/xx:]
Error 123: Customer 6: Bad Date [xx/xx:]
现在,很容易看出前两行和后两行实际上是相同的错误。问题是如何使用正则表达式实现这一点。我想将[
和]
之间的所有数字更改为x
,但我不想触摸字符串的其余部分,因此我不想将错误或客户编号转换为x
我首先尝试:
$error =~ s/(\[.*?)\d/$1x/g;
但这只涉及括号中的第一个数字。我在没有非贪婪限定符的情况下尝试过,但这只涉及最后一个字符
我可以这样做:
$error =~ s/\d/x/g;
但这会将所有出现的数字替换为x
销毁我的错误号和客户号
我可以一次又一次地传递错误行,直到不再有替换:
while ( my $error = <DATA> ) {
chomp $error;
while ( $error =~ s/(\[.*?)\d/$1x/ ) {
1;
}
say qq(Error: "$error");
}
while(我的$error=){
chomp$错误;
而($error=~s/(\[.*?)\d/$1x/){
1.
}
说qq(错误:$Error);
}
但是必须有一种方法可以做到这一点,而不必在中循环多次
是否有一种方法可以有效地将所有出现的数字替换为x
,但只能替换为两个方括号之间的数字?我会使用以下解决方案:
$error =~ s{(\[ [^\]]+ \])}{
(my $date = $1) =~ tr/0-9/x/;
$date;
}ex;
如果没有可重入的正则表达式引擎,这在旧的perl中是行不通的。显然,我错了。我用一个新的perl 5.10.1尝试了这段代码,结果很好
或者,您可以滥用左值substr
:
if ($error =~ /\[/gc) {
my $start = pos $error;
my $length = index($error, ']', $start) - $start;
substr($error, $start, $length) =~ tr/0-9/x/;
}
您不能一次完成所有操作。您需要提取要替换的部分,应用替换,然后重新生成字符串
if (
my ($pre, $date, $post) =
/^ ( [^\[\]]* \[ )( [^\[\]]* )( \] .* )/x
) {
$date =~ s/[0-9]/x/g;
$_ = "$pre$date$post";
}
这可以做得更简洁
s{ ( \[ [^\[\]]* \] ) }
{ ( my $x = $1 ) =~ s{[0-9]}{x}g; $x }xeg;
或者如果你有5.14分
s{ ( \[ [^\[\]]* \] ) }
{ $1 =~ s{[0-9]}{x}rg }xeg;
我总是喜欢将这些问题分解成更简单的部分:
sub xdigit
{
my $str= shift ;
$str =~ tr/[0-9]/xxxxxxxxxx/ ;
"[$str]"
}
my $x= 'Error 123: Customer 2: Bad Date [17/12/2014]' ;
$x =~ s/\[(.*?)\]/xdigit($1)/e ;
产出:
错误123:客户2:错误日期[xx/xx/xxxx]
您可以使用:
$error =~ / \[ /gx;
$error =~ s/ \G (.*?) [0-9] /$1x/gx;
使用修饰符/g
的搜索操作最初将锚点(即下一次搜索的起点)定位在匹配字符串的后面。然后替换操作从此点(\g
)开始搜索并替换其后面的某个位置的第一个数字。由于/g
,此外,锚点移动到被替换的数字后面,搜索+替换将重复,直到字符串结束(或者,使用([^]]*?)
而不是(.*)
,直到第一个结束括号)
在第一次尝试中,括号只找到一次;第一次替换将锚移动到被替换的数字后面,下一次搜索无法找到括号。使用重新“调试”代码>看到锚移动。Perl需要有多新才能处理这个问题?我认为在此之前可以在替换表达式中使用正则表达式,只是不要在(?{…})
和(?{…})和(?{…})
@ikegami中使用正则表达式。看起来你在这方面是对的,它在旧的Perl上运行得很好。@NathanielWaisbrot我错了,这在较旧的perl上运行良好。此外,您可以使用tr/0-9/x/
而不是s/[0-9]/x/g
;两者都可以工作,但是tr//
的效率会稍微高一点。实际上我并没有忽略它。我要给客户举一个错误的例子,日期是[21-10-2013]
,所以我也不会报告[24-02-2013]
的日期。这些可能来自同一个错误。但是,如果还报告了日期[12-10:]
,则客户站点中可能还有另一个错误。我想向客户报告这一点,但如果还有[08-13://code>,则不需要报告。其思想是给出每种日期错误类型的示例。这就是为什么我需要[…]
格式,而不是实际的数字。
$error =~ / \[ /gx;
$error =~ s/ \G (.*?) [0-9] /$1x/gx;