我的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
答案 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年之间的数据,这可能会很重要。