tags:

views:

159

answers:

2

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

   untie @lines; 
}
+2  A: 

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.

Zaid
+1  A: 

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?

lindes
Thanks for your help. I wasn't able to test the last suggestion because the script gives me an error "Can't locate List/MoreUtils.pm in @INC (@INC contains: C:/Perl64/site/lib C:/Perl64/lib .)" I'm using ActivePerl 5.10 and Win7(x64). I'm quite satisfied with the other solutions and that is what I was looking for but I'm curious what could be the reason for that error.
thebourneid
these swaps could be written using array slices: `@lines[$i, $i+1] = @lines[$i+1, $i];`
Eric Strom
Eric Strom: Good call, thanks for pointing that out. Definitely tidier that way. thebourneid: Are you familiar with CPAN? It's the "Comprehensive Perl Archive Network", and it is your friend. :-) I'm unfamiliar with it in an ActivePerl setting, so I'll point you to http://www.google.com/search?q=ActivePerl+cpan to figure out how to use it. Once you're in the CPAN shell, though, you should (hopefully) be able to just type `install List::MoreUtils` and get it that way. Or just use the other options, that's fine too.
lindes
Open up a command prompt and run `ppm`. Look for the `List-MoreUtils` package and install it.
rjh
@thebourneid => i'd encourage you to take a look at strawberry perl which has the advantage of a fully working cpan client on windows, which means you can install the latest modules from cpan (including the ones that require compilation), rather than the limited subset of modules that activestate's ppm supports
Eric Strom