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

I am hoping for some pixie dust, a magic pattern-matching flag or just simply a smoother way to conserve casing during a substitution. The idea is such that foo|Foo|FOO would be "smartly" replaced by bar|Bar|BAR in a single clean substitution.

My hokey solution to this is to use a rudimentary hash thusly:

%hash = ( 'foo' => 'bar', 'Foo' => 'Bar', 'FOO' => 'BAR', 'foO' => 'baR', 'Is' => 'Was', ); $string =~ s/(foO)/$hash{$1}/;
but this becomes cumbersome as soon as I get weary of maintaining the %hash when adding new patterns.

Replies are listed 'Best First'.
Re: Pattern Matching: Case-Conservation
by dave_the_m (Monsignor) on Jun 02, 2004 at 22:13 UTC
    $s = 'foo FOO Foo fOO'; $s =~ s/(foo)/$1 ^ lc($1) ^ "bar"/gie; print $s,"\n"; # outputs "bar BAR Bar bAR"
    This works as long as you are using ASCII and only matching alpha characters

    Dave.

      Now that is some slick piece of coding... and exactly what I was looking for (in so many words). Big thanks!

      "Dogs love me cuz I'm crazy sniffable; I bet you never knew I got the ill peripherals."
      -- Beastie Boys

Re: Pattern Matching: Case-Conservation
by duff (Parson) on Jun 02, 2004 at 22:09 UTC

    perldoc -f case

    Here's the first part of the text from my docs:

    How do I substitute case insensitively on the LHS while preserving case on the RHS?

    Here's a lovely Perlish solution by Larry Rosler. It exploits properties of bitwise xor on ASCII strings.

    $_= "this is a TEsT case"; $old = 'test'; $new = 'success'; s{(\Q$old\E)} { uc $new | (uc $1 ^ $1) . (uc(substr $1, -1) ^ substr $1, -1) x (length($new) - length $1) }egi; print;

    But you really should read the rest of the entry

    update: I just realized that I may have slightly misread what you wanted to do. That's okay though as the technique (using the ^ operator) is basically the same. :-)

Re: Pattern Matching: Case-Conservation
by davido (Cardinal) on Jun 03, 2004 at 05:12 UTC
    Here's yet another way to do it. Don't be too distracted by the /x modifier which allows for whitespace in the RE. I just did that to hopefully make it a little easier to read.

    All the 'work' is done inside the right side of the s/// operator. @array is there just to demonstrate that it works on every possible combination of foo.

    use strict; use warnings; my @array = qw/foo Foo FOo FOO fOO fOo foO FoO/; my $repl = 'bar'; print "@array\n"; s{ \b(foo)\b }{ join '', map { substr( substr( $1, $_, 1 ) ge 'a' ? lc $repl : uc $repl, $_, 1 ) } 0 .. $+[0] }eix foreach @array; print "@array\n";

    Enjoy!


    Dave

Re: Pattern Matching: Case-Conservation
by jfroebe (Parson) on Jun 02, 2004 at 22:07 UTC

    Something like this?

    my %hash = ( 'foo' => 'bar', 'Foo' => 'Bar', 'FOO' => 'BAR', ); my $string = "foo Foo foo FOO"; print "old: $string\n"; foreach (keys %hash) { $string =~ s/($_)/$hash{$1}/g; } print "new: $string\n";
    old: foo Foo foo FOO
    new: bar Bar bar BAR

    Jason L. Froebe

    Update: I should have read the question more carefully. Follow the second reply (written by Duff).. this one would do better for having a list of tokens that would be replaced with the real values.

    No one has seen what you have seen, and until that happens, we're all going to think that you're nuts. - Jack O'Neil, Stargate SG-1

Re: Pattern Matching: Case-Conservation
by andyf (Pilgrim) on Jun 02, 2004 at 22:21 UTC
    I think maybe a logical problem in your question needs clarification.

    If you replace X with Y thats fine
    If you replace X OR Y with Z that is also fine logic
    But you cannot wish to replace X OR Y with V OR W.
    You can have 2 rules mapping X to V and Y to W, but not within the same substitution.
    The lvalue of a substitution may be a choice The rvalue cannot be
    I think you want to use 'map'. However heres a suggestion.
    Sounds like the problem is that you want semanitic (meaning) of the word match, which is a separate issue from the capitalisation of letters. Store lowercase only versions of the strings, and a binary sequence coresponding to capitalisation as a hash pair

    eg.
    DooD -> dood , 1001
    RanDoMcAPS -> randomcaps, 1001010111
    Use your lc strings to do the matching (which might be mildly happier for a smaller search space) and the capitalisation 'bumpmap' to regenerate the original.

    There is also a very elegant way to derive and reapply the 'bumpmap' digits, which I'm sure is an easy exercise in Perl.
Re: Pattern Matching: Case-Conservation
by TomDLux (Vicar) on Jun 03, 2004 at 02:04 UTC

    Before you read any further, remember to smile, or the pixie dust won't do any good!

    Option 1
    • Close your eyes.
    • Click your heels three times.
    • Repeat after me, "There's no place like home. There's no place like home. There's no place like home. "
    Option 2
    Use emacs, it can preserve the case of the found pattern in the replacement string.
    Option 3
    Follow duff's advice

    --
    TTTATCGGTCGTTATATAGATGTTTGCA