Perl: How to replace only matched part of string?

25,309

Solution 1

There's another way, and it's quite simple:

my $str = "foo_bar_not_needed_string_part_123";
$str =~ s/(?<=foo_bar_)\D+//gi;
print $str;

The trick is to use lookbehind check anchor, and replace all non-digit symbols that follow this anchor (not a symbol). Basically, with this pattern you match only the symbols you need to be removed, hence no need for capturing groups.

As a sidenote, in the original regex (?=bar)bar construct is redundant. The first part (lookahead) will match only if some position is followed by 'bar' - but that's exactly what's checked with non-lookahead part of the pattern.

Solution 2

You can capture the parts you do not want to remove:

my $str = "foo_bar_not_needed_string_part_123";
$str =~ s/(foo_bar_).*?(_\d+)/$1$2/;
print $str;

Solution 3

You can try this:

my $str = "foo_bar_not_needed_string_part_123";

say $str if $str =~ s/(foo_(?=bar)bar_).*?(_\d+)/$1$2/;

Outputs:

foo_bar__123

PS: I am new to perl/regex so I am interested if there exist a way to directly replace the matched part. What I have done is captured everything which is required and than replaced the whole string with it.

Solution 4

go with

echo "foo_bar_not_needed_string_part_123" | perl -pe 's/(?<=foo_bar_)[^\d]+//'

Solution 5

You can use look-behind/look-ahead in this case

$str =~ s/(?<=foo_bar_).*?(?=_\d+)//;

and the look-behind can be replace with \K (keep) to make it a little tidier

$str =~ s/foo_bar_\K.*?(?=_\d+)//;
Share:
25,309
RanRag
Author by

RanRag

I am an undergraduate student who has completed his B-Tech in Information &amp; Communication Technology(ICT), INDIA. I am just a normal guy who has recently found interest in programming. Initially I was a big Java fan but now I am a Python enthusiast. Currently, exploring Android App Development.

Updated on July 09, 2022

Comments

  • RanRag
    RanRag almost 2 years

    I have a string foo_bar_not_needed_string_part_123. Now in this string I want to remove not_needed_string_part only when foo_ is followed by bar.

    I used the below regex:

    my $str = "foo_bar_not_needed_string_part_123";
    
    say $str if $str =~ s/foo_(?=bar)bar_(.*?)_\d+//;
    

    But it removed the whole string and just prints a newline.

    So, what I need is to remove only the matched (.*?) part. So, that the output is

    foo_bar__123.
    
  • RanRag
    RanRag over 11 years
    Nice, way to get this done. Thanks, for the redundant part info.
  • RanRag
    RanRag over 11 years
    Can you clarify one thing is look-behind/look-ahead capturing or non-capturing. For:eq st=foobar and I use foo(?=bar) it will not capture bar in $1 unless I use foo(?=bar)(bar) than I have bar in $1. So, is there any way I can make them capturing.
  • Gene D.
    Gene D. over 11 years
    I can't comment not mine post, but anyway: foo(?=bar)(bar) has no sense, since it looks for "bar" after "foo" (but DOES NOT includes it, just looking, if "bar" is here, then "foo" included in result, if "bar" is absent, then "foo" does not included), and then includes bar. foo(bar) should works fine, "bar" will be in $1.
  • raina77ow
    raina77ow over 11 years
    The construct (?=%pattern%)%pattern% is redundant by definition: it's the same as ([a-c]|a|b|c), for example. In both cases you check for the same pattern twice. If you need to capture the part that follows foo, just use foo(bar) syntax without any lookahead checks: it will match only if foo is followed by bar.
  • RanRag
    RanRag over 11 years
    @Noob: If you want to capture the value in look-ahead you can do foo(?=(bar)). Although the example you presented is redundant.
  • Borodin
    Borodin over 11 years
    @raina77ow: this doesn't strictly match the requirement, as the OP said he wanted to retain the underscore before the trailing digits
  • Borodin
    Borodin over 11 years
    This sort of thing has long been superseded by look-ahead / look-behind
  • raina77ow
    raina77ow over 11 years
    @Borodin That would be quite easy to fix (the easiest is just to change ...\D+//gi to ...\D+/_/gi). But I thought it was actually an artifact of the code given in the question, and not the real intent. )
  • Breiz
    Breiz almost 11 years
    @Borodin, yes and no. Lookbehind has some serious limitations (e.g. many regex flavors, including those used by Perl and Python, only allow fixed-length strings) and I'm glad that tanguy mentioned this too.