Perl中有条件替换的正则表达式替换

时间:2019-02-21 15:58:53

标签: regex perl

我的Perl技能非常初级,我正在尝试使用正则表达式替换(除其他外)将标量变量中加载的数据文件中的日期转换为四位数年份。

我有以下工作可以使所有年份增加20。

$data00 =~ s/^D(\d{2})\/(\d{2})\/(\d{2})\n/D$1\/$2\/20$3\n/gm;

但是,日期包括2000年之前的日期。

在寻找解决方案时,我遇到了/ e选项,该选项表示它将替代项作为Perl代码进行评估。但是我在所有的文档中都没有找到它,我不确定语法是什么。

是否有一种方法可以评估$ 3的匹配项,如果$ 3小于50,则输出20,得出2000;如果不是,则得出19,从而得出1997?我选择了50,因为它似乎是安全的中间地带。

出于说明目的,尽管我知道这是不正确的:

$data00 =~ s/^D(\d{2})\/(\d{2})\/(\d{2})\n/D$1\/$2\/(if($3<50)20 else 19)$3\n/eg;

在这种情况下,/ e是否合适?

从巨大的文本文件中提取的行示例。

D04/07/97
D04/14/98
D10/06/99
D10/13/05
D03/04/10
D12/09/10
D01/20/11
D12/22/11

2 个答案:

答案 0 :(得分:3)

使用<body aria-live="assertive" aria-checked="true"> <form role="search"> <input type="search" name="q" placeholder="Search query" aria-label="Search through site content"> <input type="submit" value="Go!"> </form> </body>时,替换表达式必须是有效的Perl表达式(即,您可以放在 # Your huge list will be input.txt # Your station list will be input2.txt In [3]: inp1 = open('input.txt') In [4]: inp2 = open('input2.txt') # if you don't want to hold anything in memory then this will be hacky solution, memory consuption is also less with open('input') as inp1: for i in inp1: if any([i.startswith(j) for j in inp2]): print(i) # Result 25MA MIDAS4 2013.3717 2019.1075 5.7358 2007 1279 1398 -0.010216 0.016478 299C MIDAS4 2003.0308 2007.0856 4.0548 1407 1407 2159 -0.003861 -0.021031 # if you want to do some kind of work on filtered data it is better to store it in memory In [5]: inp1 = {i.split(' ',1)[0] :i.split(' ',1)[1] for i in inp1} # The above lines read your huge file and convert into key-value pair dict # result will be something like this. In [6]: inp1 Out[6]: {'1ULM': 'MIDAS4 2003.4497 2019.1075 15.6578 5496 4984 7928 -0.013284 -0.000795\n', '20NA': 'MIDAS4 2008.2355 2017.4511 9.2156 2793 2793 5010 0.031619 0.059160\n', '21NA': 'MIDAS4 2008.2355 2017.4648 9.2293 3287 3287 5891 0.031598 0.059243\n', '25MA': 'MIDAS4 2013.3717 2019.1075 5.7358 2007 1279 1398 -0.010216 0.016478\n', '299C': 'MIDAS4 2003.0308 2007.0856 4.0548 1407 1407 2159 -0.003861 -0.021031\n', '2TRY': 'MIDAS4 2012.0465 2013.6564 1.6099 564 437 437 0.018726 0.054083'} # similarly, we are going to do for the station file but slightly a different data structure In [22]: inp2 = set([i.strip() for i in inp2]) # inp2 will look like In [23]: inp2 Out[23]: {'25MA', '299C'} # so to get your result filter the input list based on the station set. In [24]: res = {k:v for k,v in inp1.items() if k in inp2} In [25]: res Out[25]: {'25MA': 'MIDAS4 2013.3717 2019.1075 5.7358 2007 1279 1398 -0.010216 0.016478\n', '299C': 'MIDAS4 2003.0308 2007.0856 4.0548 1407 1407 2159 -0.003861 -0.021031\n'} # Hope this answer helps you 之后的内容)。

您可以使用条件运算符(/e)根据条件对表达式进行不同的计算:

$x =

请注意,当涉及到许多?:时,替换定界符会使内容更具可读性。

s/^D(\d{2})\/(\d{2})\/(\d{2})\n/ "D$1\/$2\/".( $3 < 50 ? 20 : 19 )."$3\n" /eg

答案 1 :(得分:1)

我会用Time::Piece来做到这一点。使用strptime()类方法将日期解析为一个对象,然后使用strftime()对其进行格式化。

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';
use Time::Piece;

while (<DATA>) {
  chomp;

  my $date = Time::Piece->strptime($_, 'D%m/%d/%y');

  say $date->strftime('D%m/%d/%Y');
}

__DATA__
D04/07/97
D04/14/98
D10/06/99
D10/13/05
D03/04/10
D12/09/10
D01/20/11
D12/22/11

输出:

D04/07/1997
D04/14/1998
D10/06/1999
D10/13/2005
D03/04/2010
D12/09/2010
D01/20/2011
D12/22/2011

可以通过以下方法简化正则表达式解决方案:a)选择其他定界符,b)使用三元运算符。如果您使用/e,则替换文本必须在语法上是有效的Perl。

while (<DATA>) {
  chomp;

  s|D(\d{2}/\d{2}/)(\d{2})|"D$1" . ($2 < 50 ? '20' : '19') . $2|e;

  say;
}

更新:两种解决方案之间存在一个(可能是重要的)区别-当从两位数年份转换为四位数年份时,介于20世纪和21世纪之间。正则表达式解决方案使用50(如原始问题中所述)。 Time :: Piece解决方案使用69-该限制是硬编码的,因此无法更改它。对于原始问题中的数据,这没有什么区别。但是,如果您拥有年份在1950年到1969年之间的数据,这可能会很重要。