1

我正在编写一个 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 的解决方案持开放态度。

4

3 回答 3

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
于 2012-12-19T19:47:29.247 回答
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.

于 2012-12-19T19:25:56.333 回答
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 具有三种标准数据类型,标量(类似 的变量$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文件的行,并使用您需要的数据修改它们。

于 2012-12-19T19:51:26.397 回答