2012-11-23 147 views
3

所有日期我有以下文件:查找和替换文件

20120127.221500.std|MT:63|ST:1.|ON:ABT.N|DRT:U|SEQ:862461707 
     80 Bezahlt  : 55.04 
     81 Bezahlt_Umsatz : 200 
    281 Bezahlt_Zeit : 22:00:02 
    752 Quelle   : CTS OTC 
     83 Umsatz_gesamt : 5639295 
    621 VWAP   : 54.984104 
     26 Zeit   : 22:00:05 

    20120127.232408.std|MT:63|ST:1.|ON:ABT.N|DRT:U|SEQ:862507497 
     41 Schluss  : 55.02 
    120 Schluss_Datum : 27.01.2012 

    20120128.011558.std|MT:63|ST:1.|ON:ABT.N|DRT:U|SEQ:862559511 
     25 Datum   : 28.01.2012 
     26 Zeit   : 01:01:30 

我希望能够找到的所有日期(即27.01.2012,28.01.2012)并更换最新的一个(即28.01.2012)与今天的日期。我希望用较旧的日期替换所有较旧的日期。我向你展示一个例子,因为我认为你最好能理解我。假设今天是21.11.2012。我希望2012年1月21日2012年11月21日,2012年1月27日以及20.11.2012取代2012年1月28日。如果有26.01.2012我想用19.11.2012替换它。

任何人都可以给我线索我该怎么办?

也许一些提示算法看起来应该如何?我很乐意在Perl中做到这一点。

我的问题是如何确定最早的日期。我已经开始了类似的东西:

open F ,"<$file"; 
    my $content = do{local $/;<F> }; 
    if ($content =~ /BOERSEN : [N|Q]/) 
    { 
     $content =~ /(\d\d\.\d\d\.\d\d\d\d)/; 
     my $d = $1; 
     my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); 
     $year+= 1900; 
     $mon +=1; 
     $mon = sprintf("%02d", $mon); 
     $content =~ s/(\d\d)\.\d\d\.\d\d\d\d/$1\.$mon\.$year/msgi; 
     my @d = split (/\./, $d); 
     $d = $d[2].$d[1]; 
     $content =~ s/$d(\d\d)/$year$mon$1/msgi; 
    } 

但它不是真的是我想要的。

+1

是他们都在同DD.MM.YYYY格式? – amphibient

+0

不是真的。但我设法识别格式。我需要的是弄清楚如何以我描述的方式替换日期。我的问题是,有更多的日期。 – MaMu

+1

你尝试过什么吗?你举了一个例子很好,但如果你向我们展示了你的奋斗目标,那将会更好。 – simbabque

回答

3

我愚弄了一下,想出了这个。它需要先读取完整的输入,然后才能正常工作。

#!/usr/bin/perl 
use strict; use warnings; 
use DateTime; 
use DateTime::Format::Strptime; 

my $text = <<'TEXT'; 
foo 27.01.2012 27-01-2012 
foo 28.01.2012 28-01-2012 
foo 26.01.2012 26-01-2012 
bar 10.07.2011 10-07-2011 
TEXT 

# Formatter to make DateTime objects 
my $strp = DateTime::Format::Strptime->new(
    pattern => '%d.%m.%Y', 
); 
my $today = DateTime->today; # we need that to calculate 

# Get all the dates from the input and turn them into DateTime objects 
my %dates = map { $_ => $strp->parse_datetime($_) } 
    $text =~ m/(\d{2}\.\d{2}.\d{4})/gm; 

# Determine the latest date (the one nearest to today) and clone it 
my $max_date = (sort { DateTime->compare(@dates{$a, $b}) } keys %dates)[-1]; 
$max_date = $dates{$max_date}->clone; 

foreach my $date (keys %dates) { 
    # The new value needs to have the same "distance" to today as the old one 
    # had to the highest date from the input 

    # Do that calculation and format it 
    my $new_date = $strp->format_datetime(
     $today - ($max_date - $dates{$date})); 
    # Needs \Q and \E because there are '.' in the date 
    $text =~ s/\Q$date\E/$new_date/g; 
} 

下面是输出:

foo 22.11.2012 27-01-2012 
foo 23.11.2012 28-01-2012 
foo 21.11.2012 26-01-2012 
bar 05.05.2012 10-07-2011 
+0

这是一个日志文件,我担心它可能*巨大*,并将它全部读入内存可能是一个问题。我还希望看到一个'while'循环,它可以找到文本中的所有日期,并一次性更改它们,而不是每次都为每个不同的日期进行全局替换。 – Borodin

2

CPAN上有日期和时间模块的批次

你会需要找到一个可以很容易地N天添加日期。使用POSIX模块的mktimestrftime模块和POSIX::strptime模块的strptime可能就足够了。

您需要通过指定想要成为当前日期的“旧日期”来确定N.您可以计算两个日期(旧日期和当前日期)之间的差异,以天为单位给出一个整数值N.然后对于每个日期行,提取日期部分,向其添加N天,并重写日期部分与新的假日期。


您询问确定'最早'的日期。您显示的格式基于ISO 8601,这意味着诸如20120127之类的字符串可以按字符串或数字排序,以给出日期顺序。您似乎也有一个日志文件;在这样的文件中,第一个日期通常是最早的,而最后一个日期是最新的,因为它们以单调递增的时间顺序顺序写入。

+0

好吧,它不是真正的日志文件,日期是一个字符串。我想我必须检查每一行以确定最早的日期。 – MaMu

1

这里是处理文件的一些指针:

open F ,"<$file"; 
my $content = do{local $/;<F> }; 
close(F); 

my $DATE_RE = qr/((\dd)\.(\d\d)\.(\d\d\d\d))/; 
my %jdate; 
# Find all of the dates and convert them to date ordinals 
while ($content =~ m/$DATE_RE/g) { 
    $jdate{$1} ||= jdate($2, $3, $4); 
} 

# find the most recent date 
my $latest; 
for my $d (keys %jdate) { 
    if (!$latest || $jdate{$latest} < $jdate{$d}) { 
    $latest = $d 
    } 
} 

# for each date $d, determine what to replace it with 
my %replacement; 
for my $d (keys %jdate) { 
    $replacement{$d} = ...your code here... 
} 

# Replace all of the dates 
$content =~ s/$DATE_RE/$replacement{$1}/ge; 

# done! 

的关键是功能jdate(...)其中日 - 月 - 年转换成整数。 在CPAN上有很多模块可以做到这一点 - 例如Time::JulianDay

来确定日期的替代,你可以使用inverse_julian_day()功能,儒略日序号转换为日 - 月 - 年的三倍,即类似的:

my ($y, $m, $d) = inverse_julian_day($today_jd - ($jdate{$latest} - $jdate{$d})); 
$replacement{$d} = sprintf("%02d.%02d.%04", $d, $m, $y); 
2

Time::Piece模块是该满意目的,它是一个核心模块,所以不需要安装。

该程序抓取当前的日期和时间,然后将时间字段设置为零,方法是将其格式化为%d.%m.%Y字符串并将其读回。然后打开并读取日志文件,查看所有日期和找到最新的一个。计算文件中最新日期与当前日期之间的增量,并将文件倒回到开头并再次读取。此时每个日期都会将计算出的增量添加到该日期,并且在输出中替换字符串。

use strict; 
use warnings; 

use Time::Piece(); 
use Fcntl ':seek'; 

my $today = Time::Piece->new; 
$today = Time::Piece->strptime($today->dmy('.'), '%d.%m.%Y'); 

open my $fh, '<', 'logfile.txt' or die $!; 

my $latest = 0; 

while (<$fh>) { 
    if (/:\s*(\d\d\.\d\d\.\d\d\d\d)/) { 
    my $date = Time::Piece->strptime($1, '%d.%m.%Y'); 
    $latest = $date if $date > $latest; 
    } 
} 

my $delta = $today - $latest; 
seek $fh, 0, SEEK_SET; 

while (<$fh>) { 

    s{:\s*\K(\d\d\.\d\d\.\d\d\d\d)}{ 
    my $date = Time::Piece->strptime($1, '%d.%m.%Y'); 
    $date += $delta; 
    $date->dmy('.'); 
    }eg; 

    print; 
} 

输出

20120127.221500.std|MT:63|ST:1.|ON:ABT.N|DRT:U|SEQ:862461707 
    80 Bezahlt  : 55.04 
    81 Bezahlt_Umsatz : 200 
281 Bezahlt_Zeit : 22:00:02 
752 Quelle   : CTS OTC 
    83 Umsatz_gesamt : 5639295 
621 VWAP   : 54.984104 
    26 Zeit   : 22:00:05 

20120127.232408.std|MT:63|ST:1.|ON:ABT.N|DRT:U|SEQ:862507497 
    41 Schluss  : 55.02 
120 Schluss_Datum : 22.11.2012 

20120128.011558.std|MT:63|ST:1.|ON:ABT.N|DRT:U|SEQ:862559511 
    25 Datum   : 23.11.2012 
    26 Zeit   : 01:01:30