KVon
KVon

Reputation: 13

How to match multiple groups on a specific line in Perl, but not other lines?

I understand the /g flag and I'm able to match a pattern multiple times, but I'm having a difficult time researching this particular question.

Say my string has multiple lines and I want to match a grouped pattern on a line that contains a specific word, but not on a different line that doesn't.

For an example string $test with multiple lines

Unimportant line.
Important line with !special1! word and !special2! word. 
Extra line with !special3! word and !special4! word.

I want to print the two specials on the important line but not the extra line, so the result would be:

special1, special2  

I can match every special using /g

my @both = $test =~ /!(.+?)!/g;
print join(', ', @both);

But the output is:

special1, special2, special3, special4

If I try to include the word 'Important'

my @both = $test =~ /Important.+?!(.+?)!/g;
print join(', ', @both);

I just get

special1

I understand it only matches once because 'Important' only occurs once, but I can't figure out how to get the output

special1, special2

Upvotes: 1

Views: 104

Answers (3)

zdim
zdim

Reputation: 66899

There are a number of ways to do this. I think the most solid and flexible approach is to break your string into lines and iterate over that array. Then you can choose how to select what you need and what you want to do with it, at every iteration. For example

my @lines = split '\n', $test;

foreach my $line (@lines)
{
    next if $line !~ /^Important/;

    my @all_on_line = $line =~ /!(.+?)!/g;

    if (@all_on_line) 
    {
        print join(', ', @all_on_line), "\n";
    }
}

I assumed that Important is at the beginning of the wanted line. Adjust as needed. Since it is likely that more is done with data (than just print it) the if condition checks whether that line actually had the pattern.

Packing this into a single regex is quite a bit more involved.

Upvotes: 2

Borodin
Borodin

Reputation: 126742

You can do this in two steps. First isolate the Important line from the string, and then search for the special substrings

Here's an example. It uses for as a topicaliser, and is essentially doing ($_) = $test =~ /^(.*Important.*)$/m for every occurrence of Important

Note that this code will report the data from the last line containing Important if there is more than one

use strict;
use warnings 'all';

my $test = <<END;
Unimportant line.
Important line with !special1! word and !special2! word. 
Extra line with !special3! word and !special4! word.
END

my @both;
@both = /!([^!]+)!/g for $test =~ /^(.*Important.*)$/m;

print join(', ', @both), "\n";

output

special1, special2

Upvotes: 1

ssr1012
ssr1012

Reputation: 2589

This is also helpful with mapping function:

my @nwlines = map{ $_=~m/^Important(.+?(?=\!special).+?)$/mi } @lines;

Upvotes: 0

Related Questions