使用正则表达式从日志文件行中提取特定数据,然后与来自其他行的时间戳进行比较并生成csv文件

时间:2014-12-01 15:28:02

标签: regex perl csv scripting timestamp

我是Perl的新手,我将数据合并到一个新文件时遇到了问题。

我有一个日志文件(下面的示例),包括带有(RSSI值和时间戳)的行和带有(GPS位置(纬度,经度)和时间戳的行)。 GPS位置的触发频率高于RSSI值,因此日志文件包含的GPS线位数多于RSSI值的线位数。

我必须找到具有与每个RSSI时间戳匹配的正确时间戳的GPS线。

时间戳有99:99:99.999格式。要比较时间戳,我只需要99:99:99格式。

最后,我想生成一个新的CSV文件,其时间戳为99:99:99格式,RSSI值和相应的GPS位置,丢弃无用的GPS位置。 CSV文件应包含(时间戳,RSSI,纬度,经度)。日志文件如下所示。 (在这个例子中,GPS位置不会改变,但实际上它们会改变。)

# .log file
# first rssi timestamp is 10:23:05.746 (hour:minute:second.microsecond) and RSSI value -91
Line 167: 0.11036     0   25.11.2014 10:23:05.746  01.01.1970 00:53:55.545  CON.NAD.CINTERION     nadProc.NAD_Run_AT_Cmds         info   55     NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -91 -91
Line 1747: 0.12577     0   25.11.2014 10:23:07.967  01.01.1970 00:53:57.766  CON.NAD.CINTERION     nadProc.NAD_Run_AT_Cmds         info   55     NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -92 -92
Line 2096: 0.12925     0   25.11.2014 10:23:11.744  01.01.1970 00:54:01.544  CON.NAD.CINTERION     nadProc.NAD_Run_AT_Cmds         info   55     NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -93 -93
Line 3472: 0.14272     0   25.11.2014 10:23:15.745  01.01.1970 00:54:05.545  CON.NAD.CINTERION     nadProc.NAD_Run_AT_Cmds         info   55     NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -92 -92
Line 4915: 0.15681     0   25.11.2014 10:23:17.965  01.01.1970 00:54:07.766  CON.NAD.CINTERION     nadProc.NAD_Run_AT_Cmds         info   55     NADCinterion::handleSMONI(3441): SMONI LTE_RSSI -94 -94
# first GPS timestamp is 10:23:05.716 (hour:minute:second.microsecond) and GPS position 11.38172906823456,48.78751751035452
Line 194: 0.11062     0   25.11.2014 10:23:05.716  01.01.1970 00:53:55.515  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 819: 0.11672     0   25.11.2014 10:23:06.715  01.01.1970 00:53:56.514  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 1443: 0.12281     0   25.11.2014 10:23:07.716  01.01.1970 00:53:57.515  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 2538: 0.13558     0   25.11.2014 10:23:08.714  01.01.1970 00:54:04.514  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 2738: 0.13558     0   25.11.2014 10:23:11.714  01.01.1970 00:54:04.514  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 3324: 0.14130     0   25.11.2014 10:23:15.714  01.01.1970 00:54:05.514  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 4261: 0.15048     0   25.11.2014 10:23:17.114  01.01.1970 00:54:06.914  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
Line 4694: 0.15466     0   25.11.2014 10:23:17.813  01.01.1970 00:54:07.614  BL.POSITIONING        navi.DEF_THR                    debug  98     PositionNLAdapter < pos:11.38172906823456,48.78751751035452 heading: 156.0113220214844 speed:0km/h
  1. 我必须使用LTE_RSSI $line =~ /RSSI/,然后从该行$lines_rssi获得正确的时间戳$rssi_timestamp和带有正则表达式的$lte_rssi m/(\-\d+$)/ $line =~ m/BL.POSITIONING/ 1}}。

  2. 获取GPS位置和时间戳BL.POSITIONING $lines_gps的正确行,然后从该行$gps_timestamp获取时间戳$latitude和相应的GPS位置$longitudem/pos:(\d+\.\d+),(\d+\.\d+)/正则表达式99:99:99.999

  3. 将时间戳格式从99:99:99格式化为#!/usr/bin/perl use strict; use warnings; print "Geben Sie den Namen der log Datei ein: " ; my $log =<STDIN>; open(LOG, $log) || die "Log nicht gefunden"; my $rssi_timestamp; #rssi timestamp in format dd:dd:dd.ddd my $lte_rssi; #rssi value my $gps_timestamp; #gps timestamp ind format dd:dd:dd.ddd my $latitude; #gps latitude my $longitude; #gps longitude my $lines_rssi; #one complete line with rssi my $lines_gps; #one complete line with gps my $gps_timestamp_mod; #modified gps timestamp in format dd:dd:dd my $rssi_timestamp_mod; #modified rssi timestamp in format dd:dd:dd while (my $line = <LOG>) { if ($line =~ /RSSI/) #find right line containing rssi value (regex RSSI) { $lines_rssi = $line; $lines_rssi =~ m/(\d{2}\:\d{2}\:\d{2}\.\d{3})/; #find matching timestamp pattern with regex $rssi_timestamp = $1; print "$rssi_timestamp,"; $rssi_timestamp =~ m/(\d{2}\:\d{2}\:\d{2})/; #modify rssi timestamp format from dd:dd:dd.ddd to dd:dd:dd $rssi_timestamp_mod = $1; print "$rssi_timestamp_mod,"; $lines_rssi =~ m/(\-\d+$)/; #find rssi value with regex $lte_rssi = $1; print "$lte_rssi\n"; } if ($line =~ m/BL.POSITIONING/) #find line with GPS position with regex { $lines_gps = $line; $lines_gps =~ m/(\d{2}\:\d{2}\:\d{2}\.\d{3})/; # find matching timestamp in gps line $gps_timestamp = $1; $gps_timestamp =~ m/(\d{2}\:\d{2}\:\d{2})/; $gps_timestamp_mod = $1; if ($gps_timestamp_mod == $rssi_timestamp_mod) # here i want to compare the 2 modified timestamps with each other to find the right gps line but it doesn't work { $lines_gps =~ m/pos:(\d+\.\d+),(\d+\.\d+)/; $latitude = $2; $longitude = $1; print "$gps_timestamp,$gps_timestamp_mod,$latitude,$longitude\n"; } } } 进行比较。

  4. 从RSSI线获取第一个修改后的时间戳,并将其与GPS线的修改时间戳进行比较,并在匹配时间戳时,从该线获取此GPS位置。

  5. 打印出具有修改的RSSI时间戳,RSSI值,匹配GPS线的GPS位置的CSV文件(参见第4点)。

  6. 我的代码看起来像这样。它打印出所有RSSI时间戳和值,以及所有GPS时间戳和位置,但不会将时间戳相互比较。然后只打印一个时间戳,RSSI值,纬度,经度

    10:23:05,-91,48.78751751035452,11.38172906823456
    10:23:07,-92,48.78751751035452,11.38172906823456
    10:23:11,-93,48.78751751035452,11.38172906823456
    10:23:15,-92,48.78751751035452,11.38172906823456
    10:23:17,-94,48.78751751035452,11.38172906823456
    

    CSV文件应如下(timestamp,rssi,latitude,longitude)。

    只有来自RSSI值的时间戳应该在这里(10:23:05,10:23:07,10:23:11,10:23:15,10:23:17)丢弃带有时间戳的GPS线路(10:23:06,10:23:08,以及10:23:17之一):

    {{1}}

    有人可以帮我解决这个问题吗?

    @Borodin:非常感谢代码。它完美无缺! 此外,正如你所提到的,我也在考虑GPS坐标的插值,就像在第2页的{1}和(2)Polynomial interpolation of GPS satellite coordinates

    这个文件中所解释的一样。

    代码中的插值有多复杂?

1 个答案:

答案 0 :(得分:1)

我认为这可以满足您的需求。我没有删除小数秒,而是编写了一个子程序epoch_seconds,它使用Time::Piece模块将时间(包括毫秒)日期转换为浮点数 - 从1970年开始以来的秒数。这将避免在午夜时分出现任何问题。

读取整个文件,相关数据存储在两个哈希值中:%rssi%gps,由浮点时间戳索引。然后,使用List::UtilsBy中的%rssi函数,通过查找时间戳中绝对差异最小的元素,将%gps的每个元素与min_by元素配对。然后,只需要在找到的两个元素中打印所有数据。

您可能必须安装List::UtilsBy,因为它不是核心模块。

请注意,输出中实际报告的时间戳是$report_time设置的值。我已经删除了日期和小数秒,使其与您的示例相同,但您可以根据需要修改它。

我觉得在RSSI数据两侧的GPS坐标之间进行线性插值以获得更好的准确度是很简单的,如果有用的话。

我希望这会有所帮助。

use strict;
use warnings;

use Time::Piece;
use List::UtilsBy qw/ min_by /;

print "Geben Sie den Namen der log Datei ein: ";
chomp(my $log_file = <STDIN>);
open my $log_fh, '<', $log_file or die "Log nicht gefunden: $!";

my $out_file = 'logfile.csv';

my (%rssi, %gps);

while (<$log_fh>) {

  next if /^#/;

  if (/LTE_RSSI/) {     # find right line containing rssi value (regex RSSI)

    next unless /( \d+\.\d+\.\d+ \s+ \d+:\d+:\d+\.\d+ )/x;
    my $timestamp = $1;
    my $timestamp_seconds = epoch_seconds($timestamp);
    my ($report_time) = $timestamp =~ /(\d+:\d+:\d+)/;

    next unless /(\-\d+$)/;    # find RSSI value with regex
    $rssi{$timestamp_seconds} = [$report_time, $1];
  }
  elsif (/BL\.POSITIONING/) {    # find line with GPS position with regex

    next unless /( \d+\.\d+\.\d+ \s+ \d+:\d+:\d+\.\d+ )/x;
    my $timestamp = $1;
    my $timestamp_seconds = epoch_seconds($timestamp);

    next unless /pos:(\d+\.\d+),(\d+\.\d+)/;
    $gps{$timestamp_seconds} = [$2,$1];
  }
}

open my $out_fh, '>', $out_file or die qq{Unable to open "$out_file" for output: $!};

for my $rssi_seconds (sort { $a <=> $b } keys %rssi) {
  my $gps_seconds = min_by { abs($_ - $rssi_seconds) } keys %gps;
  print $out_fh join(',', @{ $rssi{$rssi_seconds} }, @{ $gps{$gps_seconds} }), "\n";
}

sub epoch_seconds {
  my ($date_time) = @_;
  die unless shift =~ /(.+)\.(.+)/;
  Time::Piece->strptime($1, '%d.%m.%Y %H:%M:%S')->epoch . ".$2";
}

<强>输出

10:23:05,-91,48.78751751035452,11.38172906823456
10:23:07,-92,48.78751751035452,11.38172906823456
10:23:11,-93,48.78751751035452,11.38172906823456
10:23:15,-92,48.78751751035452,11.38172906823456
10:23:17,-94,48.78751751035452,11.38172906823456