如何使用perl从文件中删除类似的重定义

时间:2012-09-29 05:15:16

标签: perl shell perl-module

我有一个文件中包含以下数据

typedef INTEGER Id;
typedef Integer32 Id;
typedef Integer32 Id;
typedef Integer32 Id;
typedef INTEGER Identifier;
typedef Integer32 Index;
typedef Unsigned32 Identifier;
typedef Integer32 Index;
typedef Unsigned32 TunnelId;
typedef Unsigned32 TunnelId;
const Unsigned32 maxValue = 65535;
const Integer32 Index_maxValue = 65535;        
const Unsigned32 maxValue = 4294967295;
const Unsigned32 Index_maxValue = 65535;

我需要按以下顺序选择变量: 如果变量有......

  • INTEGERInteger32定义 - 必须选择Integer32
  • Integer32Unsigned32定义 - 必须选择Unsigned32
  • INTEGERUnsigned32定义 - 必须选择Unsigned32

预期输出:

typedef Integer32 Id;
typedef Unsigned32 Identifier;
typedef Integer32 Index;
typedef Unsigned32 TunnelId;
const Unsigned32 maxValue = 65535;
const Unsigned32 Index_maxValue = 65535;

我写了下面这段代码...... 但它没有产生上述预期结果。

@file2 = @file // full content of the file in an array
for ($i = 0; $i <= $#file; $i++) {
   $temp = $file[$i];
   $check = $file[$i];
   $check =~ s/^\s+//;

   @fields = split(/ /, $check);

   @grepNames = grep(/$fields[2]/, @file2);

   if($#grepNames >= 1) {

      for ($j = 1; $j <= $#file; $j++) {
         if( $file[$i] =~ /INTEGER/ && $file[$j] =~ /Unsigned32/ ) {
            push(@data, $file[$j]);
         }
         elsif( $file[$i] =~ /INTEGER/ && $file[$j] =~ /Integer32/ ) {
            push(@data, $file[j]);
         }
         elsif( $file[$i] =~ /Unsigned32/ && $file[$j] =~ /Integer32/ ) {
            push(@data, $file[i]);
         }
    }
}

2 个答案:

答案 0 :(得分:3)

尽管你已经接受了 0%的问题(提出这个数字是非常公平的),这里是对你的代码的分析,这样你就可以写得更好(和工作)下次Perl。

关于编写Perl的一般性评论

纪律与混乱

始终use strictuse warnings,尤其是当您试图弄清楚脚本无法正常工作或您不熟悉Perl的原因时。通常,这些编译指示会提醒您每个人偶尔会犯的大多数愚蠢错误。

Perl并没有强迫你编写好的代码(和TIM TOWDTY),但大多数时候你应该坚持使用Perl的strict子集,除非你有一个非常好的原因。

使用strict意味着您必须使用my声明所有变量,除非您有充分的理由。我认为声明变量是一件好事。

Perl不是C

大多数内置函数不需要用于分隔其参数的parens。即<{1}}和split(/ /, $check)在大多数情况下是相同的。

此外,Perl很少需要split / /, $check循环结构,尤其是当最后一部分为for(INIT; COMPARE; INCREMENT)时。相反,您可以使用$i++ - 语法和范围

foreach

为什么您的代码不起作用

并且:可以避免什么不好的习语

我已经指出所有变量都应该用for my $i ($MIN .. $MAX) 声明,并且有更好的循环语法可用。

my

@file2 = @file // full content of the file in an array 没有引入评论。它是 defined-或运算符。此外,此语句不会被//终止。这会使Perl感到困惑,因为以下;循环是同一语句的一部分 - 但这是无效的。

此外,您不会更改for@file2的内容,因此无需复制。

@file

您永远不会使用$temp = $file[$i];

$temp

使用此@grepNames = grep(/$fields[2]/, @file2); 来查找包含相同变量名的行数。当您稍后再次遍历grep中的所有元素时,这是超级流畅的。

@file

你写的是问题: if($#grepNames >= 1) 中的最高索引是否大于或等于1?而你可能意味着我们有多个匹配吗? 。我把它写成

@grepNames

然而,这主要是一种风格评论。

if (@grepNames > 1)

等什么?如果if( $file[$i] =~ /INTEGER/ ... 包含$file[$i],则INTEGER$check也会包含$temp。使用标量而不是数组下标时,可以节省一些输入。

push(@data, $file[$j]);

即使您已经有一个具有相同变量名称的行,也可以将某些内容推送到@data。更糟糕的是,如果@file包含 n 元素,那么内部for循环将迭代 n - 1 元素,并将某些内容推送到{{ 1}}在大多数情况下,使算法 O(n²)

@data

这是一个表(根据您的规则)表明哪个元素应该进入 i \ j | INTEGER | Int32 | UInt32 --------+---------+-------+------- INTEGER | - | j | j Int32 | i | - | j UInt32 | i | i | - 的表格。您可能希望将此表与@data / if进行比较,并确定是否可能缺少某些案例。

elsif

您在push(@data, $file[j]); 之前忘记了$ sigil。

更好的解决方案

您的算法在 O(n²)中运行,或者更确切地说是 O(n *(2n - 1))。但是,您的问题可以在 O(n)中解决。

我将问题视为:

  

输入中的每一行都有一个标识符和一个相关的权重。

     

对于输出,要选择具有相同标识符的所有行的集合中具有最小权重的行。如果两条或更多条线具有相同的最小权重,则可以选择这些最小线中的任何一条。

     

行的权重取决于行中第二个单词的关键字:

j
     

如果第二个单词不是这些关键字,则应该抛出错误。

     

每个标识符的第一次次出现的顺序在输入和输出中都是相同的。

在我的解决方案中(如下所示),我使用第1个和第3个单词作为标识符。如果输出中已存在一行,则在当前行的权重较低时更新它。


编辑:我的解决方案

Unsigned32 => 1,
Integer32  => 2,
INTEGER    => 3,

输出:

#!/usr/bin/perl

use strict; use warnings;

my @data;
my %index;

while (<DATA>) {
   $_ =~ s/^\s+//;
   my @fields = split /\s+/, $_, 3;
   @fields = (@fields[0 .. 1], split /(?=\W)/, $fields[2], 2);
   my ($class, $type, $name, $rest) = @fields;
   if (defined $index{$class}{$name}) {
      # we have a predecessor
      my $index = $index{$class}{$name};
      $data[$index][1] = (sort compareTypes $data[$index][1], $type)[0];
   } else {
      push @data, \@fields;
      $index{$class}{$name} = $#data;
   }
}

foreach (@data)  {
   my @fields = @$_;
   print "@fields[0..2]$fields[3]";
}

sub compareTypes {
   my %weight = (
      Unsigned32 => 1,
      Integer32  => 2,
      INTEGER    => 3,
   );
   my $weight_a = $weight{$a} // die "undefined type $a";
   my $weight_b = $weight{$b} // die "undefined type $b";
   return $weight_a <=> $weight_b;
}

__DATA__
typedef INTEGER Id;
typedef Integer32 Id;
typedef Integer32 Id;
typedef Integer32 Id;
typedef INTEGER Identifier;
typedef Integer32 Index;
typedef Unsigned32 Identifier;
typedef Integer32 Index;
typedef Unsigned32 TunnelId;
typedef Unsigned32 TunnelId;
const Unsigned32 maxValue = 65535;
const Integer32 Index_maxValue = 65535;        
const Unsigned32 maxValue = 4294967295;
const Unsigned32 Index_maxValue = 65535;

答案 1 :(得分:0)

解决了这个问题,附上了工作代码。

#!/opt/perl/bin/perl

open  (DF, "< UNIQ_DEF.txt");
open  (DR, "> DEF_REM.txt");
my @var = <DF>;
my @param =();
my @param2=@var;

for ($i=0; $i<=$#var;$i++)
{
    $temp=$var[$i];
    $check=$var[$i];
    $check=~s/^\s+//;

    @params=split(/ /, $check);

    if (defined $params[2]){

        @Names = grep(/$params[2]/, @param2);

        if($#Names >= 1){
            if ($Names[0] =~ /Unsigned32/ && $Names[1] =~ /Unsigned32/) {
                print DR $Names[0];
            } elsif($Names[0] =~ /INTEGER/ && $Names[1] =~ /Unsigned32/) {              
                print DR $Names[1];
            } elsif($Names[0] =~ /Integer32/ && $Names[1] =~ /Integer32/) {
                print DR $Names[1];
            } elsif($Names[0] =~ /INTEGER/ && $Names[1] =~ /Integer32/) {
                print DR $Names[1];
            } elsif($Names[0] =~ /Integer32/ && $Names[1] =~ /Unsigned32/) {
                print DR $Names[1];
            } elsif($Names[0] =~ /Unsigned32/ && $Names[1] =~ /Integer32/) {
                print DR $Names[0];
            } else {
                print DR $var[$i];
            }
        } else {
            print DR $var[$i];
        }

    } else {
        print DR $var[$i];
    }

}
close(DF);
close(DR);