1

メッセージ定義を含む 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ファイルの情報を格納することです。$fooPerl には、スカラー ( のような変数)、配列 ( のような変数@foo)、およびハッシュ ( のような変数)という 3 つの標準データ型があります%hash。これらの各データ型は、個々の値を扱います。配列とハッシュがこれらの値のリストを処理するのに対し、スカラーは 1 つの個別の値のみを含むことができます。

より複雑な構造を扱うには、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 に答える