【问题标题】:How can I swap two consecutive lines in Perl?如何在 Perl 中交换两个连续的行?
【发布时间】:2011-02-22 04:25:05
【问题描述】:

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

/^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; 
}

【问题讨论】:

  • 您在处理一个非常大的文件吗?
  • 不,通常单个 cue sheet 是 50-100 行,但每次我使用 File::find 通过目录处理它们时。发布的代码是包含多个子代码的较大代码的一部分。

标签: perl


【解决方案1】:

这似乎有点矫枉过正,但看到您的文件不是很大,我很想利用以下单行代码(从命令行或通过system 调用)。

one-liner 的工作原理是一次性将所有的行都吞掉,然后将其余的工作留给正则表达式替换,这会翻转行的顺序。

如果你使用 *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 了解更多信息指导)。

不过,在您尝试这些单行代码之前,我会为您的文件创建一个备份。

【讨论】:

    【解决方案2】:

    好吧,既然您要绑定到一个数组中,我只需检查 $lines[$i]$lines[$i+1](只要 +1 地址存在),如果前者匹配​​ TITLE 和后者 PERFORMER ,交换它们。除非即使它们不是连续的,您也可能需要转置它们??

    如果您知道它们将是连续的,这里有一个选项(这个 sn-p 将进入您的 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 语句一起)然后执行(在&amp;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]);
    }
    

    这就是你所追求的吗?

    【讨论】:

    • 感谢您的帮助。我无法测试最后一个建议,因为脚本给了我一个错误“无法在 @INC 中找到 List/MoreUtils.pm(@INC 包含: C:/Perl64/site/lib C:/Perl64/lib 。 )" 我正在使用 ActivePerl 5.10 和 Win7(x64)。我对其他解决方案非常满意,这就是我一直在寻找的解决方案,但我很好奇导致该错误的原因是什么。
    • 这些交换可以使用数组切片来编写:@lines[$i, $i+1] = @lines[$i+1, $i];
    • Eric Strom:好电话,感谢您指出这一点。这样肯定更整洁。 thebourneid:你熟悉 CPAN 吗?它是“综合 Perl 存档网络”,它是您的朋友。 :-) 我对 ActivePerl 设置中的它不熟悉,所以我会告诉你 google.com/search?q=ActivePerl+cpan 以了解如何使用它。但是,一旦您进入 CPAN shell,您应该(希望)能够只输入install List::MoreUtils 并以这种方式获得它。或者只是使用其他选项,也可以。
    • 打开命令提示符并运行ppm。查找List-MoreUtils 包并安装它。
    • @thebourneid => 我鼓励你看一下草莓 perl,它具有在 windows 上完全工作的 cpan 客户端的优势,这意味着你可以从 cpan 安装最新的模块(包括需要编译的),而不是 activestate 的 ppm 支持的有限模块子集
    猜你喜欢
    • 1970-01-01
    • 2023-03-20
    • 2010-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-06
    相关资源
    最近更新 更多