开发者

How can I swap two consecutive lines in Perl?

I have this part of a code for editing cue sheets and I don't know how to reverse two consecutive lines if found:

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

to reverse to

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

What would it be the solution in my case?

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开发者_Python百科);
             }
        }

   untie @lines; 
}


This may seem like overkill, but seeing that your files aren't very large, I'm tempted to leverage the following one-liner (either from the command line or via a system call).

The one-liner works by slurping all the lines in one shot, then leaving the rest of the work to a regex substitution which flips the order of the lines.

If you're using *nix:

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

If you're using Windows, you'll need to create a backup of the existing files:

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

Explanation

Command Switches (see perlrun for more info)

  • -0777 (an octal number) enforces file-slurping behavior
  • -i enables in-place editing (no need to splice-'n'-dice!). Windows systems require that you provide a backup extension, hence the additional .bak
  • -n loops over all lines in your file(s) (although since you're slurping them in, Perl treats the contents of each file as one line)
  • -e allows Perl to recognize code within the command-line

Regex

  • The substitution regex captures all occurrences of the TITLE line, the consecutive PERFORMER line, and stores it in variables $1 and $2 respectively. The substitution regex then flips the order of the two variables, separated with a newline.

Filename Arguments

  • You could use *nix to provide the filenames of the directories in question, but I'll leave that to someone else to figure out as I'm not too comfortable with Unix pipes just yet (see this John Siracusa answer for more guidance).

I would create a backup of your files before you try these one-liners though.


Well, since you're tying into an array, I'd just check $lines[$i] and $lines[$i+1] (as long as the +1 address exists, that is), and if the former matches TITLE and the latter PERFORMER, swap them. Unless perhaps you need to transpose these even if they're not consecutive??

Here's an option (this snippet would go inside your for loop, perhaps above the REM-checking line) if you know they'll be consecutive:

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

Another option (which would work regardless of consecutiveness, and is arguably more elegant) would be to

use List::MoreUtils qw(first_index);

(up at the top, with your other use statements) and then do (inside &c, but outside the for loop):

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]);
}

Is that what you're after?

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜