0

I use smartmatch to check whether a string matches a regex pattern. It stopped working after I decided to store the regex in a text file.

my $str   = '123456, some text.';
my $regex = qr/^\d+, some text\.$/;
print "Regex: $regex\n";

At this point, the printed regex is (?^:^\d+, some text\.$). I copy-paste it into a file, then I make the code read the file and retrieve the regex, which is stored in $regexFromFile.

The following line confirms that $regex and $regexFromFile are the same, then I proceed to test $str against the regex in various ways.

print 'Is regex equal to regexFromFile? ' . ($regex eq $regexFromFile) . "\n";

print 'Does str match regex         using =~ ? ' . ($str =~ $regex)         . "\n";
print 'Does str match regexFromFile using =~ ? ' . ($str =~ $regexFromFile) . "\n";
print 'Does str match regex         using ~~ ? ' . ($str ~~ $regex)         . "\n";
print 'Does str match regexFromFile using ~~ ? ' . ($str ~~ $regexFromFile) . "\n";

The last line of that code does not behave the same as the previous three lines.

Here is the complete output of the code:

Regex: (?^:^\d+, some text\.$)
Is regex equal to regexFromFile? 1
Does str match regex         using =~ ? 1
Does str match regexFromFile using =~ ? 1
Does str match regex         using ~~ ? 1
Does str match regexFromFile using ~~ ? 

(Note the absence of 1 at the end.)

Edit: To answer a comment, here is how the file is read.

open(my $FILEHANDLE, 'file.txt') or die "Error: Could not open file.\n";
my @content = <$FILEHANDLE>;
close($FILEHANDLE) or print "Could not close file.\n";
my @content_woEol = ();
foreach my $line (@content){
    $line =~ s/\s*$//;
    push(@content_woEol, $line);
}
my $regexFromFile = $content_woEol[0];
Georg
  • 1,078
  • 2
  • 9
  • 18

3 Answers3

4

Smartmatching is broken. Please avoid using it.[1]

$str is a string, and $regexFromFile is a string, so $str ~~ $regexFromFile is equivalent to $str eq $regexFromFile.

If you wanted $str ~~ $regexFromFile to be equivalent to $str =~ $regexFromFile, you'd have to convert the misnamed $regexFromFile from a string into a regex (e.g. using qr//). Of course, the far better solution is to simply use =~.


  1. Many years ago, it was made experimental as a form of deprecation so it could be fixed. Continued use of this broken feature actually prevented a recent effort to fix it.
ikegami
  • 367,544
  • 15
  • 269
  • 518
  • 1
    Who is downvoting any negative comments about *smartmatching*? From its inception in Perl v5.10 it was a bad idea, and [perldelta v5.10.1](https://perldoc.perl.org/5.10.1/perldelta.html#Smart-match-changes) started the backslide with `~~` becoming non-commutative, together with consideration of the range operators. It was always a mess, with hope and imagination overruling pragmatism. It was never very Perlish and it has stayed alive for far too long. – Borodin Mar 01 '18 at 16:04
  • 2
    My point is that `$str ~~ $regexFromFile` is not the same as `$str ~~ $regex`, even though `$regexFromFile eq $regex` is true... – Georg Mar 01 '18 at 19:01
  • 1
    `$regexFromFile eq $regex` compares the stringification of its operands, so `$regexFromFile eq $regex` being true only dictates that `$str ~~ "$regexFromFile"` (false) and `$str ~~ "$regex"` (false) should be the same. – ikegami Mar 01 '18 at 19:03
2

The result of qr// is actually a precompiled regular expression. Copy-pasting the printed regular expression to a file, as you did, then reading it from the file, is not the problem. You would experience the same behavior had you written this line directly in your code:

my $regexFromFile = '(?^:^\d+, some text\.$)';

If you want to use smatmatch here, I would advise you to do something like the following:

my $str   = '123456, some text.';
my $regex = '^\d+, some text\.$';

# Manually store this in the file: ^\d+, some text\.$
# Read $regexFromFile from the file

print 'Does str match regex         using =~ ? ' . ($str =~ /$regex/)         . "\n";
print 'Does str match regexFromFile using =~ ? ' . ($str =~ /$regexFromFile/) . "\n";
print 'Does str match regex         using ~~ ? ' . ($str ~~ /$regex/)         . "\n";
print 'Does str match regexFromFile using ~~ ? ' . ($str ~~ /$regexFromFile/) . "\n";

Note the additional /.../. Output:

Does str match regex         using =~ ? 1
Does str match regexFromFile using =~ ? 1
Does str match regex         using ~~ ? 1
Does str match regexFromFile using ~~ ? 1
user8487873
  • 190
  • 7
-1

Smartmatch has been experimental since v5.18 and should never have been used in live production software

It has always required

use feature 'switch';

And unless you are someone who consciously or explicitly ignores warnings, with

no warnings qw/ experimental::smartmatch /;

then you will have had ample warning of its demise

You have made an informed choice that was clearly wrong from the start

Borodin
  • 126,100
  • 9
  • 70
  • 144
  • 1
    @amon: Your comment is confusing people. My post says *"Smartmatch has been experimental since v5.18"* – Borodin Mar 01 '18 at 16:06