如何在Perl中交换两个连续的行?

时间:2010-05-21 02:45:50

标签: perl

我有一部分用于编辑提示表的代码,如果找到,我不知道如何反转两行:

/^TITLE.*?"$/  
/^PERFORMER.*?"$/

反转

/^PERFORMER.*?"$/  
/^TITLE.*?"$/  

在我的案例中,解决方案是什么?

use strict; 
use warnings; 
use File::Find; 
use Tie::File;


my $dir_target = 'test';

find(\&c, $dir_target);        
sub c {  
   /\.cue$/ or return;

   my $fn = $File::Find::name;

   tie my @lines, 'Tie::File', $fn or die "could not tie file: $!"; 
        for (my $i = 0;  $i < @lines; $i++) {
             if ($lines[$i] =~ /^REM (DATE|GENRE|REPLAYGAIN).*?$/) {
                  splice(@lines, $i, 3);
             }
     if ($lines[$i] =~ /^\s+REPLAYGAIN.*?$/) {
                  splice(@lines, $i, 1);
             }
        }

   untie @lines; 
}

2 个答案:

答案 0 :(得分:4)

这看起来有点矫枉过正,但是看到你的文件不是很大,我很想利用下面的单行(从命令行或通过system调用)。

单行通过一次性啜饮所有行来工作,然后将剩下的工作留给正则表达式替换,这将取代行的顺序。

如果你正在使用* nix:

perl -0777 -i -ne 's/(TITLE.*?")\n(PERFORMER.*?")/$2\n$1/g' file1 file2 ..

如果您使用的是Windows,则需要创建现有文件的备份:

perl -0777 -i.bak -ne "s/(TITLE.*?\")\n(PERFORMER.*?\")/$2\n$1/g" file1 file2 ..

解释

命令开关(有关详细信息,请参阅perlrun

  • -0777(八进制数字)强制执行文件诽谤行为
  • -i支持就地编辑(无需splice - 'n'-dice!)。 Windows系统要求您提供备份扩展,因此需要额外的.bak
  • -n循环遍历文件中的所有行(尽管由于您正在将它们插入,Perl会将每个文件的内容视为一行)
  • -e允许Perl识别命令行中的代码

正则表达式

  • 替换正则表达式捕获所有出现的TITLE行,即连续的PERFORMER行,并分别将其存储在变量$1$2中。替换正则表达式然后翻转两个变量的顺序,用换行符分隔。

文件名参数

  • 您可以使用* nix来提供相关目录的文件名,但我会将其留给其他人来解决,因为我对Unix管道不太熟悉(请参阅此John Siracusa answer更多指导)。

在尝试使用这些单行程序之前,我会创建一个文件备份。

答案 1 :(得分:0)

好吧,既然你正在加入一个数组,我只需检查$lines[$i]$lines[$i+1](只要+1地址存在,就是这样),如果前者匹配​​TITLE和后者的PERFORMER交换它们。除非你需要转换它们,即使它们不是连续的吗?

这是一个选项(如果你知道它们是连续的,那么这个代码片段会进入你的for循环,可能在REM检查行之上):

if ($i < $#lines and $lines[$i] =~ /^TITLE.*?"$/
                 and $lines[$i+1] =~ /^PERFORMER.*?$/) {
    my $tmp = $lines[$i];
    $lines[$i] = $lines[$i+1];
    $lines[$i+1] = $tmp;
}

另一种选择(无论连续性如何都可以使用,并且可以说更优雅)将是

use List::MoreUtils qw(first_index);

(在顶部,使用其他use语句)然后执行(在&c内部,但在for循环之外):

my $title_idx = first_index { /^TITLE.*?"$/ } @lines;
my $performer_idx = first_index { /^PERFORMER.*?"$/ } @lines;

if($title_idx >= 0 and $performer_idx >= 0 and $title_idx < $performer_idx)
{
    # swap these lines:
    ($lines[$title_idx],$lines[$performer_idx]) =
        ($lines[$performer_idx],$lines[$title_idx]);
}

这就是你要追求的吗?