views:

210

answers:

6

Hi

I am reading each line of an input file (IN) and printing the line read to an output file (OUT) if the line begins with one of the patterns, say "ab", "cd","ef","gh","ij" etc. The line printed is of form "pattern: 100" or form "pattern: 100:200". I need to replace "pattern" with "myPattern", i.e. print the current line to FILE but replace all the text before the first occurence of ":" with "myPattern". What is the best way to do this?

Currently I have:

while ( <IN> )
{ 
    print FILE if /^ab:|^bc:|^ef:|^gh:/;
}

I am not sure if substr replacement would help as "pattern" can be either "ab" or"cd" or "ef" or "gh" etc.

Thanks! Bi

A: 
while ( <IN> )
{ 
  s/^pattern:/myPattern:/;
  print OUT
}
That prints every line regardless of whether it matches the pattern in the first place. As I read the original question, only matching lines should be transformed and written to OUT
A. Levy
+1  A: 

Perl's substitution operator by default (a) uses the first match, (b) only replaces one match and (c) returns true if a replacement was made and false if it wasn't.

So:

while ( <IN> )
{ 
    if (s/<pattern1>:/<replace1>/ ||
        s/<pattern2>:/<replace2>/) {
       print FILE;
    }
}

Should work for you. Note that because of short-circuiting, only one substitution will be made.

YGA
Note that you will strip off the first colon as well with this subsitution.
Nic Gibson
not if replace1 and replace2 have colons too
YGA
A: 

This might be what you want:

$expr = "^(ab)|(cd)|(ef)|(gh)|(ij)";
while (<IN>)
{
    if (/$expr:/)
    {
        s/$expr/$myPattern/;
        print FILE;
    }
}
A. Levy
A: 
sub replacer {

 $line = shift;
 $find = shift;
 $replace = shift;

 $line =~ /([^:]+):/
 if ($1 =~ /$find/) { 
   $line =~ s/([^:]+):/$replace/ ;
   return $line;  
 }
 return ;

}

while (<IN>)
{
 print OUT replacer ($_,"mean","variance");
 print OUT replacer ($_,"pattern","newPattern");
}

My perl is a little rusty, so syntax might not be exact.

edit: Put it in a function for ya.

Byron Whitlock
Thanks Byron - This is what I need as the value of "myPattern" varies based on the value in "pattern" i.e "ab" or "cd" or "ef". Can I do this as a one-liner?
Bi
$line =~ /([^:]+):/ What does the colon mean above? Thanks.
Bi
[^:]+ - means anything that doesn't match a colon.
Byron Whitlock
thanks - how do i pass something like "mean(" to the sub function? I tried print OUT replacer ($_,"mean\(","variance"); and print OUT replacer ($_,"mean(","variance");both throw error - Unmatched ( in regex; Thanks.
Bi
you might need to double \\( since it is in quotes before it is passed to the function.
Byron Whitlock
+1  A: 

The shortest way to do what you ask above is to re-use your code, but include a substitution.

while ( <IN> )
{ 
    print FILE if s/^(ab|bc|ef|gh):/MyPattern:/;
}

Any of the left hand side patterns will be replaced. If the left hand side does not match, nothing will be printed.

Alex Brown
+3  A: 

Generically, do this like:

my %subst = ( 'ab' => 'newab', 'bc' => 'newbc', 'xy' => 'newxy' );
my $regex = join( '|', map quotemeta, sort { length($b) <=> length($a) } keys %subst );
$regex = qr/^($regex):/;

while ( <IN> ) {
    print FILE if s/$regex/$subst{$1}:/;
}

The sort puts the longest ones first, so that if the data has ab:: and both ab and ab: are being substituted, ab: is used instead of ab.

ysth