Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw
 
PerlMonks  

Search and Replace

by Anonymous Monk
on Jul 19, 2000 at 19:35 UTC ( #23207=perlquestion: print w/replies, xml ) Need Help??

Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I would like to search a log file for a string enclosed in "()" then I would like to replace all ";" in the string with "," but only the string within the "()". The ";" outside of the "()" should be left alone. Example: Before: Bla bla bla; bla bla (foo; bar); bla bla After: Bla bla bla; bla bla (foo, bar); bla bla Is there a way to do this?

Replies are listed 'Best First'.
Re: Search and Replace
by tye (Sage) on Jul 19, 2000 at 19:52 UTC
    s#([(][^)]+[)])#{my$x=$1;$x=~s/;/,/g;$x}#e

    I'm sure there are several ways to shorten that, if anyone wants to play Perl Golf on it.

    (Note that the original question said "for a string" not "for all strings", so I'm not adding a final g to the above, even though that would probably match what they really wanted as opposed to what they asked for.) ;)

Re: Search and Replace
by infoninja (Friar) on Jul 19, 2000 at 19:59 UTC
    You can shorten that to:
    s#(\([^)]+\))#{$_=$1;s/;/,/g;$_}#eg
    (not a huge difference, but I've had no coffee today...)
    update:Fixed to add g modifier to end (see turnstep's post below)
RE: Search and Replace
by japhy (Canon) on Jul 19, 2000 at 21:10 UTC
    I think we should benefit from Perl 5.6's (??{ ... }) regex expression.
    $REx = qr{ \( (?: (?> [^()]+) | (??{ $REx }) )* \) }x; $str =~ s/($REx)/(my $a = $1) =~ tr!;!,!; $a/eg;
    This makes it work for nested parens, too, like "hi; there; (fools; (all))"
Re: Search and Replace
by greenhorn (Sexton) on Jul 19, 2000 at 21:00 UTC
    My test string was:
    $s = "Bla (bar;) bla bla; bla bla (foo; bar); bla bla Bla bla bla; bla bla (foo; bar); bla bla Bla bla bla; bla bla (foo; bar); bla bla Bla bla (bar;foo;bar) bla; bla bla (foo; bar); bla bla Bla bla bla; bla bla (foo; bar); bla bla ";

    (It probably isn't a good idea to "embed" newlines in that way, but doing so worked ok for test purposes just now.)

    The regular expression I used was similar to what Ovid suggests:

    $s =~ s/\(([^)]*);([^)]*)\)/($1,$2)/g;

    The difference was in the placement of the parens. I decided to store "$1" and "$2" inside the literal parens and then re-insert the literal parens so that they'd kind of jump out at me--what's happening in the substitution would be slightly more obvious to me if I had to look at it some time later. Maybe. :)

    The regular expression breaks down as shown below. Note that in the following I've added the modifier "x", which doesn't appear above. "x" should be used if you want to break the regular expression onto several lines and add comments, as it's done here:

    s/ # search for... \( # a literal "(" ( # begin storing what follows (in "$1") [^)]* # zero or more of anything that is *NOT* ")" ) # stop storing in "$1" ; # a semicolon ( # begin storing what follows (in "$2") [^)]* # zero or more of anything that is *NOT* ")" ) # stop storing in "$2" \) # a literal ")" /($1,$2)/gx; # and replace all that with...see below...

    ... replace it with a literal "(", followed by whatever was stored in "$1", followed by a comma, followed by whatever was stored in "$2", followed by a literal ")".

    Modifier: "g" = everywhere within the string (see above concerning "x").

    This regular expression worked with strings such as (foo;bar) and (;bar) and (foo;), but it did not work for a string such as (foo;bar;boink).

    Note that if you were to use one of the proposed solutions containing<kbd> .*</kbd>: if your input file contains newlines, and if you're doing these replacements after "slurping" the entire file at once into a variable, for safety's sake add the "s" modifier to the substitution. (See perlre for more about the modifiers.

    Taking my lead from the mavens here: remember to "use strict;" and to check your syntax with "-w"...

Re: Search and Replace
by maverick (Curate) on Jul 19, 2000 at 20:01 UTC
    if there is a single ; to be replaced you could do
    $string =~ s/^(.*\(.*);(.*\).*)$/$1,$2/;
    if there's an unspecified number
    ($a,$b,$c) = split(/[()]/,$string); $b =~ s/;/,/g; $string = $a."(".$b.")".$c;
    there's probably a way to do it in single regexp, but I don't have enough caffeine in my blood to see it yet.

    /\/\averick

Re: Search and Replace
by mdillon (Priest) on Jul 19, 2000 at 20:04 UTC
    getting shorter...
    s#\([^)]+\)#$_=$&;y/;/,/;$_#ge
    (thanks turnstep, i added 'g'. duh!)

    (also, as le says below, it isn't a good idea to use $& in production code)

      I wouldn't use (and recommend using) $&, because of efficiency reasons. "Mastering Regular Expressions" explains why.
        thanks. i'm aware of that, but i was only interested in shortening the s///. i've read Mastering Regular Expression cover-to-cover. i can't say that i've actually ever used $& in any Perl code i've written, since i read MRE very early in my Perl days (right after Effective Perl Programming) and took the warning you're reiterating here to heart.
Re: Search and Replace
by turnstep (Parson) on Jul 19, 2000 at 20:17 UTC

    Actually, almost all of the above fail because they need a 'g' on the end. The one above by ovid fails on a case of "(foo;bar;baz)"

    And, my version, which only worries about running the second replace on those parens that have a semi-colon in them:

    s#(A[^AZ]*;+[^AZ]*Z)#{($_=$1)=~s/;/,/g;$_}#eg;
    Disclaimer: I replaced ( and ) with A and Z for increased readability. It's ugly enough already! :)

(Ovid) Re: Search and Replace
by Ovid (Cardinal) on Jul 19, 2000 at 20:12 UTC
    I'd try this:
    #!/usr/bin/perl -w use strict; my $test = "Bla bla bla;bla bla(foo; bar);bla blaBla bla bla; bla bla +(foo; bar); bla bla "; $test =~ s/(\([^;]*);([^;]*\))/$1,$2/g; print $test;
    That will replace all instances.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://23207]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (7)
As of 2022-12-08 22:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?