Grep:从一个文件中提取数据以更新另一个文件

时间:2012-12-19 18:57:03

标签: perl sed grep extract

我正在编写一个perl脚本,它使用消息定义从一个文件中提取值,并使用它们来更新配置文件。

例如:

消息定义文件(ICD.txt):

MESSAGE: FOO_TELEM
latitude [-90,90]
longitude [-180,180]
MESSAGE: FOO_FREQUENCY
airPressure [0,50]
engineSpeed [0,65500]

FOO_TELEM消息的成员纬度范围为-90到90,经度范围为-180到180.

配置文件(Config.txt):

  MessageTable
    Message FOO_TELEM
      Member latitude  DOUBLE End-Member
      Member longitude DOUBLE End-Member
    End-Message
    Message FOO_FREQUENCY
      Member airPressure ULONG End-Member
      Member engineSpeed ULONG End-Member
    End-Message
  End-MessageTable

我希望能够使用消息定义文件(ICD.txt)中包含的约束值更新配置文件(Config.txt),以便结果如下所示:

更新了配置文件(Config.txt):

  MessageTable
    Message FOO_TELEM
      Member latitude  DOUBLE CONSTRAINT -90 90 End-Member
      Member longitude DOUBLE CONSTRAINT -180 180 End-Member
    End-Message
    Message FOO_FREQUENCY
      Member airPressure ULONG CONSTRAINT 0 50 End-Member
      Member engineSpeed ULONG CONSTRAINT 0 65500 End-Member
    End-Message
  End-MessageTable

我尝试过各种形式的grep来获得我需要的结果但是没有成功。任何建议将不胜感激。我愿意接受任何涉及grep,sed和/或perl的解决方案。

3 个答案:

答案 0 :(得分:2)

这是一个镜头。我将为我的文件重复使用DATA,因为我不应该编写开/关逻辑。

use strict;
use warnings;

my %messages;
my $current;
CONSTRAINT:
while ( <DATA> ) { 
    last CONSTRAINT if m/^---$/;
    if ( my ( $message ) = m/^ MESSAGE: \s+ ( \S+ )/x ) { 
        $messages{ $message } = $current = {};
    }
    elsif ( my ( $name, $min, $max ) 
               = m/^ (\w+) \s+ \[ \s* (-?\d+), \s* (-?\d+) \s* \]/x 
          ) { 
      $current->{ $name } = [ $min, $max ];
    }   
}
while ( <DATA> ) { 
    chomp;
    if ( my ( $msg ) = m/Message \s+ ( \S+ )/x ) { 
        $current = $messages{ $msg };
    }
    elsif (   ref( $current )
          and my ( $before, $member, $after ) 
                  = m/^( \s* Member \s+ ( \w+ ) \s+ \w+ ) \s+ (.*) /x 
          ) {
        if ( my $vals = $current->{ $member } ) { 
            $_ = "$before CONSTRAINT @$vals $after";
        }
    }
    say;
}

__DATA__
MESSAGE: FOO_TELEM
latitude [-90,90]
longitude [-180,180]
MESSAGE: FOO_FREQUENCY
airPressure [0,50]
engineSpeed [0,65500]
---
MessageTable
  Message FOO_TELEM
    Member latitude  DOUBLE End-Member
    Member longitude DOUBLE End-Member
  End-Message
  Message FOO_FREQUENCY
    Member airPressure ULONG End-Member
    Member engineSpeed ULONG End-Member
  End-Message
End-MessageTable

答案 1 :(得分:1)

首先,编写解析器以将数据加载到以下数据结构中:

my %data = (
   FOO_TELEM => {
      latitude  => [  -90,  90 ],
      longitude => [ -180, 180 ],
   },
   FOO_FREQUENCY => {
      latitude  => [ 0,    50 ],
      longitude => [ 0, 65500 ],
   },
);

然后,为您的数据定义格式编写解析器。唯一的补充是当它找到$data{$message_name}{$member_name}时查找End-Member

答案 2 :(得分:0)

其中任何一种都是标准格式吗?这会有所帮助。例如,如果您的Config.txt文件是XML格式,则它看起来像这样:

<messageTable>
    <message name="FOO_TELEM">
        <member name="latitude" type="DOUBLE"/>
        <member name="longitude" type="DOUBLE"/>
    </message>
    <message name="FOO_FREQUENCY">
        <member name="airPressure" type="ULONG"/>
        <member name="engineSpeed" type="ULONG"/>
    </message>
</messageTable>

如果您的文件没有任何特定的标准格式,您可以将它们变成标准格式吗?它不一定是XML,YAML也可以。

我问的原因是Perl拥有众多模块,可以快速解析这些标准格式,使所有内容都易于操作。如果没有,您将不得不手动解析数据以提取信息。

最简单的方法是解析表并在Perl中创建一个复杂的数据结构来存储ICD.txt文件的信息。 Perl有三种标准数据类型:Scalars(变量如$foo),数组(变量如@foo)和散列(变量如%hash)。这些数据类型中的每一种都处理个别值。标量和哈希处理这些值的列表时,标量只能包含一个单独的值。

要处理更复杂的结构,您需要使用Perl References。引用允许您拥有哈希哈希值或数组数组,数组哈希值或哈希值数组等。

例如:

use strict;
use warnings;
use feature qw(say);
use autodie;
use Data::Dumper;

open my $icd_fh, "<", "icd.txt";

my %icd_data;
my $message;
while (my $line = <$icd_fh>) {
    if ($line =~ /^MESSAGE: (.*)/) {
        $message = $1;
    }
    else {
        $line =~ /(.*) \[(.*),(.*)\]/;
        my $message_type = $1;
        my $lower_limit = $2;
        my $upper_limit = $3;
        if (not exists $icd_data{$message}) {
            $icd_data{$message} = {};
        }
        $icd_data{$message}->{$message_type} = {};
        $icd_data{$message}->{$message_type}->{LOWER} = $lower_limit;
        $icd_data{$message}->{$message_type}->{UPPER} = $upper_limit;
    }
}
say Dumper \%icd_data;

这将使您的ICD数据具有以下形状:

$VAR1 = {
            'FOO_TELEM' => {
                'longitude' => {
                    'LOWER' => '-180',
                    'UPPER' => '180'
                 },
                 'latitude' => {
                      'LOWER' => '-90',
                      'UPPER' => '90'
                 }
            },
            'FOO_FREQUENCY' => {
                'airPressure' => {
                    'LOWER' => '0',
                    'UPPER' => '50'
                },
                'engineSpeed' => {
                    'LOWER' => '0',
                    'UPPER' => '65500'
                }
          }
    };

从那里,你应该能够解析Config.txt文件的行,并用你需要的数据修改它们。