2012-12-19 55 views
1

我正在編寫一個perl腳本,它從一個帶有消息定義的文件中提取值並使用它來更新配置文件。Grep:從一個文件中提取數據以更新另一個文件

例如:

消息定義文件(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 

我希望能夠upda忒與包含在消息定義文件(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的解決方案。

+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 
+0

太棒了。您的解決方案可以直接使用。謝謝。 – Karimi

1

首先,寫一個解析器來將數據加載到數據結構如下:

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

然後,寫一個解析器爲您的數據定義格式。唯一的補充是當它找到End-Member時,它會查找$data{$message_name}{$member_name}

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文件的行,並修改它們與你需要的數據。

相關問題