簡體   English   中英

如何在 perl 中匹配多行

[英]How do I match over multiple lines in perl

假設我有一個為每個模塊格式化的網表文件:

module module_name1(in1, in2,
    in3, in4, in5,
    out1, out2, out3
    out4, out5);

整個網表中有很多這樣的內容。 我希望能夠獲取模塊名稱和端口列表。 這是我到目前為止所擁有的:

use strict;
use warnings;

my $input_file = $ARGV[0];
open (my $INFILE, $input_file) or die "$input_file cannot be opened.\n";

my $outfile = "verilog.port.txt";
open (my $OUTFILE, '>', $outfile) or die "\nUnable to create $outfile\n";

my ($module_name,$port_list);

while (<>) {
  if ($_ =~ /module (\w+)\((.+)\)/m) {
    $module_name = $1;
    $port_list = $2;
    print $OUTFILE "Module Name: $module_name Port list: $port_list\n"
  }
}
close $INFILE;

close $OUTFILE;

這僅在模塊僅在 1 行中實例化時才有效。 例如,如果:

module module_name2(in1, in2, out1, out2);

我會得到類似的東西:

Module Name: module_name2 Port list: in1, in2, out1, out2

但是,如果模塊是像在我的第一個示例中那樣通過多行創建的,則我的 reg 表達式無法選擇它。 所以我想知道是否有辦法使用 perl 匹配多行。

您正在逐行閱讀文件,如果沒有這種分隔,則需要按段落(由空行分隔的塊)或整個文件閱讀它們; 否則$_只包含一行並且不會匹配。

此外, /m標志不是您要查找的內容( /m使^ / $匹配行的開頭/結尾),您需要/s使. 包括換行符(參見: perlreref文檔頁面, perlop頁面有點混亂)

按段落,這個班輪應該可以解決問題:

$ perl -l -00 -ne 'if ( /module (\w+)\((.+)\)/s) { @ports = split(/\s*,\s*/,$2); print "Module name: $1 Ports: " . join(", ", @ports)}' <<'EOF'
> module module_name1(in1, in2,
>     in3, in4, in5,
>     out1, out2, out3,
>     out4, out5);
>
>
> module module_name2(in21, in22,
>     in23, in24, in25,
>     out21, out22, out23,
>     out24, out25);
> EOF
Module name: module_name1 Ports: in1, in2, in3, in4, in5, out1, out2, out3, out4, out5
Module name: module_name2 Ports: in21, in22, in23, in24, in25, out21, out22, out23, out24, out25

您可以使用-MO=Deparse來查看整個代碼:

perl -MO=Deparse -l -00 -ne 'if ( /module (\w+)\((.+)\)/s) { @ports = split(/\s*,\s*/,$2); print "Module name: $1 Ports: " . join(", ", @ports)}'
BEGIN { $/ = ""; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    if (/module (\w+)\((.+)\)/s) {
        @ports = split(/\s*,\s*/, $2, 0);
        print "Module name: $1 Ports: " . join(', ', @ports);
    }
}

如果您沒有分隔模塊的空行,則需要一次獲取整個文件(slurp)

perl -l -0777 -ne 'while (/module (\w+)\((.+?)\);/sg) { @ports = split(/\s*,\s*/,$2); print "Module name: $1 Ports: " . join(", ", @ports)}' <<'EOF'
> module module_name1(in1, in2,
>     in3, in4, in5,
>     out1, out2, out3,
>     out4, out5);
> module module_name2(in21, in22,
>     in23, in24, in25,
>     out21, out22, out23,
>     out24, out25);
> EOF
Module name: module_name1 Ports: in1, in2, in3, in4, in5, out1, out2, out3, out4, out5
Module name: module_name2 Ports: in21, in22, in23, in24, in25, out21, out22, out23, out24, out25

同樣,您可以使用-MO=Deparse來查看發生了什么:

perl -MO=Deparse -l -0777 -ne 'while (/module (\w+)\((.+?)\);/sg) { @ports = split(/\s*,\s*/,$2); print "Module name: $1 Ports: " . join(", ", @ports)}'
BEGIN { $/ = undef; $\ = "\n"; }
LINE: while (defined($_ = <ARGV>)) {
    chomp $_;
    while (/module (\w+)\((.+?)\);/gs) {
        @ports = split(/\s*,\s*/, $2, 0);
        print "Module name: $1 Ports: " . join(', ', @ports);
    }
}

這些方法中的關鍵元素是-0標志,它在-00形式中將$/設置$/啟用段落模式的空字符串,在-0777形式-0777 $/設置$/ undef 啟用 slurp 模式(讀取整個文件)(另請參見:perlvar 手冊中的$RS 。)

一個重要的警告: -l$\\變量設置為$/ (默認情況下為“\\n”),在這種情況下,如果您希望輸出由 new 分隔,則必須在命令行中的-0之前使用它線。

對於更優雅的方法,您可以使用以下腳本:

#!/bin/perl

use warnings;
use strict;

use File::Slurp;
use Data::Dumper;

my $data = read_file($ARGV[0]);

my %modules = $data =~ /module (\w+)\((.+?)\);/sg;

$modules{$_} = [split(/\s*,\s*/, $modules{$_})] for keys(%modules);

print Dumper(\%modules);

這將為您提供一個包含所有所需信息的數據結構 - 有關實時演示,請參見https://ideone.com/BuuR8I

有關許多可能的解決方案之一,請參閱以下代碼片段

注:OP中缺少發布的數據塊,之后out3

#!/usr/bin/perl 
#
# vim: ai:ts=4:sw=4
#

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $debug = 0;          # debug flag

my $data = do { local $/; <DATA> };

$data =~ s/[ \n]+/ /g;

my @lines = split ';', $data;

say Dumper(\@lines) if $debug;

for (@lines) {
    next unless /module\s+(.*)?\((.*)\)/;
    say "Module: $1 -- Ports: $2";
}


__DATA__
module module_name1(in1, in2,
    in3, in4, in5,
    out1, out2, out3,
    out4, out5);


module module_name2(in21, in22,
    in23, in24, in25,
    out21, out22, out23,
    out24, out25);

輸出

Module: module_name1 -- Ports: in1, in2, in3, in4, in5, out1, out2, out3, out4, out5
Module: module_name2 -- Ports: in21, in22, in23, in24, in25, out21, out22, out23, out24, out25

當 perl 具有.. range operator時,我不得不同意逐行閱讀是“不合適的”。

取OP代碼並修改如下:

while (<>) {
    if (/module/ .. /\)/) {
        $module_name = $1 if /module\s+(\w+)/;
        my $done=/\)/;
        s/.*\(//; s/\).*//;s/,\s+/, /g;
        chomp;
        $port_list .= $_;
        print $OUTFILE "Module Name: $module_name Port list $port_list\n" if $done;
    }
}

換句話說,從匹配/module/的行到匹配/)/ ,累積端口列表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM