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

Hi,
Is there a shorter way of writing the following piece of code? I need the string or variable $line to contain all those words in order to print it out. Actually my purpose is to parse the $line, but for simplicity I just want to print for now.
if(($line =~ /name/)&&($line =~ /name/)&&($line =~ /timestamp/)&&($lin +e =~ /dl_retransmits_pct/)&&($line =~ /dl_throughput/)&&($line =~ /ul +_retransmits_pct/)&&($line =~ /ul_throughput/)&&($line =~ /mac/)){ print $line."\n"; }

Replies are listed 'Best First'.
Re: Matching words
by Fletch (Bishop) on Feb 27, 2022 at 08:41 UTC

    Going by presumably the same problem (Solved: Parsing JSON) this isn't the way to handle JSON. If you have JSON you use a parser to turn it back into a perl data structure (a nested hashref) and then you extract things from that. Trying to pull lines out piecemeal from it is . . . suboptimal, let's say, at best.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: Matching words
by roboticus (Chancellor) on Feb 27, 2022 at 14:44 UTC

    PerlMonger79:

    I'm assuming you really mean simpler or easier to maintain rather than just shorter. I'd suggest something like this:

    Roboticus@Waubli ~ $ cat t.pl use strict; use warnings; # Valid records must have these fields my @required = qw( name timestamp dl_retransmits_pct dl_throughout ); while (my $line = <DATA>) { my @missing = grep { $line !~ /$_/ } @required; if (@missing) { print "record $. missing fields: ", join(", ", @missing), ".\n +"; } } __DATA__ name timestamp dl_retransmits_pct dl_throughout foo name dl_throughout timestamp dl_retransmits_pct name timestamp dl_retransmits_pct dl_throughout Roboticus@Waubli ~ $ perl t.pl record 3 missing fields: dl_throughout. record 4 missing fields: name, timestamp, dl_retransmits_pct.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Matching words
by graff (Chancellor) on Feb 27, 2022 at 18:17 UTC
    Here's another way, basically a variant of what roboticus posted (the key point being to use an array to keep the target words in one easily maintained place). This one uses a single capturing regex to handle all terms at once, and reports the hits instead of the misses:
    use strict; use warnings; my @required = qw( name timestamp dl_retransmits_pct dl_throughput mac + ); my $req_regx = join( '|', @required ); while (my $line = <DATA>) { my %matched = map{ $_ => undef } $line =~ /($req_regx)/g; print $line if scalar( keys %matched ) == scalar( @required ); } __DATA__ mac timestamp name dl_retransmits_pct dl_throughput is a hit mac timestamp name dl_retransmits_pct throughout is a miss name timestamp dl_retransmits_pct dl_throughput is a miss foo mac name dl_throughput name timestamp name dl_retransmits_pct mac +mac is a hit name timestamp dl_retransmits_pct is a miss dl_throughput is a miss
    But as Fletch suggested, if your input is really JSON data, you should use a JSON parser and handle the input as the data structure that it really is.

    (updated to fix grammar and spelling)

      my $req_regx = join( '|', @required );

      This is ok for the "pure" words given in the example, but there may be trouble if naughty regex metacharacters like * + ? ( ) [ ] etc. infiltrate the word list. See haukex's article Building Regex Alternations Dynamically for a thorough discussion of this technique.


      Give a man a fish:  <%-{-{-{-<

Re: Matching words
by Anonymous Monk on Feb 27, 2022 at 12:16 UTC