Perl: named capturing group defaults at replacement string
Asked Answered
W

2

5

The following Perl code fails on the second attempt of dereferencing the capturing group "ext"

use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

How can I set a default for the capturing group within the replacement string in case it doesn't get defined?

I'm sure there are several other ways to solve this problem, but not being able to set a default value for a capture group will surely come up another time again I believe - so please help me figure this out.

FOLLOWUP:

..I got it to work with the help of ikegami's advice, so that it reads

$out =~ s{(?:(?<ext>\.[1-9]?)|\.0)0([^\w]|$)}{ $+{'ext'} // "" }eg;

...it's just isn't there another way? Especially since this only works with Perl's "e"valuate regex functionality. This must come up in standard regex way as well, to at least ignore the capture group dereferencing if it wasn't captured in the first place, no?

Wideawake answered 3/7, 2023 at 16:11 Comment(2)
Re "isnt there another way?", There are no other way to "use a default for the capturing group". You necessarily need to run code. You can't make a computer program do something without code. You want to run this code for reach replacement, and e is how you run code to generate the replacement.Ciel
What are you trying to do ?? Why are you using the //e anyway ? What exactly are you trying to replace ? Whats the purpose of capture group 2 there ?Seminar
S
2

If you insist on using Named groups for this...
Instead of using the e modifier, just define an empty ext in the alternation context.

use strict;
use warnings;

foreach(qw/2.1 2/){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|(?<ext>)\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

Output

:2.1:
:2:

Even easier, do away with the named capture and use a Branch Reset.
Keep it portable in usage.

use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?|(\.[1-9]?)|()\.0)0([^\w]|$)/$1/g;
    print ":".$out.":\n";
}

Same output

:2.1:
:2:

See the accepted answer here for details: How to avoid warnings in Perl regex substitution with alternatives?

Seminar answered 3/7, 2023 at 23:39 Comment(5)
Or no warnings qw( uninitialized );Ciel
Oh, gone for all, or regex only ? Or turn on / off / on ?Seminar
You can scope it to where you need.Ciel
Yes, to me this is the best answer the branchreset and then the empty capturing group in the alternation - i didnt know empty capturing was possible , neither did i know you could use same named capturing groups...thanks a lot for the insite Chears!!Wideawake
@mcaustria, I purposefully didn't include that in my answer because 1. It's not what you asked, and 2. It's a bad way of achieving what you want. My answer has much cleaner solutions.Ciel
C
6

Make the replacement part an expression using the e modifier.

s{...}{ $+{ext} // "default" }eg

However, what you are trying to achieve does not require the use of captures, much less conditional captures and default values for them.

It appears that you are trying to remove all trailing zeroes, and the trailing . that might remains afterwards. For that, you could use the following simple code:

my $out = sprintf( "%.2f", $_ ) =~ s/0+\z//r =~ s/\.\z//r;

Another way to look at it is that only two substrings can be removed: .00 at the end, or 0 at the end. This gives us the following solution:

my $out = sprintf( "%.2f", $_ ) =~ s/\.00\z|0\z//r;
Ciel answered 3/7, 2023 at 16:13 Comment(5)
..thanks for the quick reply @Ciel !! It work, but is ther also a way to ignore the capture group all together if it wasnt captured in the first palce?Wideawake
"Ignore a variable" is not a sentence that make sense. (The only time a programs will ignore a variable is if you don't use it.) Please clarify what you mean.Ciel
as i mentioned in the beginning i wasnt looking for a more elegant way of solving my issue, more the possibilities of regex to handle the issue of an umdefined capturing group. I also like your solution but since its a perl only solution i gave this one to sin, Still thank you!!Wideawake
Re "its a perl only solution", uh, so is the problem (the fact that using $+{ext} warns when undeifned). Again, any solution will be language specific. Because regex can't do anything. And you want something to happen. /// Re "i gave this one to sin", which doesn't set a default. He just provides a less elegant way of solving your problem. You just said you didn't want a more elegant solution that doesn't provide a default, so it's weird you accept a less elegant one that doesn't provide a default.Ciel
@ikegami: Put a backslash before the period in s/.00\z|0\z//r?Twelve
S
2

If you insist on using Named groups for this...
Instead of using the e modifier, just define an empty ext in the alternation context.

use strict;
use warnings;

foreach(qw/2.1 2/){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?:(?<ext>\.[1-9]?)|(?<ext>)\.0)0([^\w]|$)/$+{'ext'}/g;
    print ":".$out.":\n";
}

Output

:2.1:
:2:

Even easier, do away with the named capture and use a Branch Reset.
Keep it portable in usage.

use strict;
use warnings;

foreach(qw/2.1 2/){
#foreach(@ARGV){
    my $out= sprintf("%.2f", $_);
    $out =~ s/(?|(\.[1-9]?)|()\.0)0([^\w]|$)/$1/g;
    print ":".$out.":\n";
}

Same output

:2.1:
:2:

See the accepted answer here for details: How to avoid warnings in Perl regex substitution with alternatives?

Seminar answered 3/7, 2023 at 23:39 Comment(5)
Or no warnings qw( uninitialized );Ciel
Oh, gone for all, or regex only ? Or turn on / off / on ?Seminar
You can scope it to where you need.Ciel
Yes, to me this is the best answer the branchreset and then the empty capturing group in the alternation - i didnt know empty capturing was possible , neither did i know you could use same named capturing groups...thanks a lot for the insite Chears!!Wideawake
@mcaustria, I purposefully didn't include that in my answer because 1. It's not what you asked, and 2. It's a bad way of achieving what you want. My answer has much cleaner solutions.Ciel

© 2022 - 2024 — McMap. All rights reserved.