Perl regex exclude optional word from match

Viewed 608

I have a strings and need to extract only icnnumbers/numbers from them.

icnnumber:9876AB54321_IN
number:987654321FR
icnnumber:987654321YQ

I need to extract below data from above example.

9876AB54321
987654321FR
987654321YQ

Here is my regex, but its working for first line of data.

(icnnumber|number):(\w+)(?:_IN)

How can I have expression which would match for three set of data.

6 Answers

Given your strings to extract are only upper case and numeric, why use \w when that also matches _?

How about just matching:

#!/usr/bin/env perl

use strict;
use warnings;

while (<DATA>) {
   m/number:([A-Z0-9]+)/;
   print "$1\n";
}

__DATA__
icnnumber:9876AB54321_IN
number:987654321FR
icnnumber:987654321YQ

Another alternative to get only the values as a match using \K to reset the match buffer

\b(?:icn)?number:\K[^\W_]+

Regex demo | Perl demo

For example

my $str = 'icnnumber:9876AB54321_IN
number:987654321FR
icnnumber:987654321YQ';

while($str =~ /\b(?:icn)?number:\K[^\W_]+/g ) {
  print $& . "\n";
}

Output

9876AB54321
987654321FR
987654321YQ

You may replace \w (that matches letters, digits and underscores) with [^\W_] that is almost the same, but does not match underscores:

(icnnumber|number):([^\W_]+)

See the regex demo.

If you want to make sure icnnumber and number are matched as whole words, you may add a word boundary at the start:

\b(icnnumber|number):([^\W_]+)
^^

You may even refactor the pattern a bit in order not to repeat number using an optional non-capturing group, see below:

\b((?:icn)?number):([^\W_]+)
   ^^^^^^^^

Pattern details

  • \b - a word boundary (immediately to the right, there must be start of string or a char other than letter, digit or _)
  • ((?:icn)?number) - Group 1: an optional sequence of icn substring and then number substring
  • : - a : char
  • ([^\W_]+) - Group 2: one or more letters or digits.

Just another suggestion maybe, but if your strings are always valid, you may consider just to split on a character class and pull the second index from the resulting array:

my $string= "number:987654321FR";
my @part = (split /[:_]/, $string)[1];
print @part

Or for the whole array of strings:

@Array = ("icnnumber:9876AB54321_IN", "number:987654321FR", "icnnumber:987654321YQ");

foreach (@Array)
{
    my $el = (split /[:_]/, $_)[1];
    print "$el\n"
}

Results in:

9876AB54321
987654321FR
987654321YQ

Regular expression can have 'icn' as an option and part of the interest is 11 characters after :.

my $re = qr/(icn)?number:(.{11})/;

Test code snippet

use strict;
use warnings;
use feature 'say';

my $re = qr/(icn)?number:(.{11})/;

while(<DATA>) {
    say $2 if /$re/;
}

__DATA__
icnnumber:9876AB54321_IN
number:987654321FR
icnnumber:987654321YQ

Output

9876AB54321
987654321FR
987654321YQ

Already you got best and better answers here anyway I trying to solve your question right now.

Get the whole string,

my $str = do { local $/; <DATA> }; #print $str;

You can check the first grouping method upto _ or \b from the below line,

@arrs = ($str=~m/number\:((?:(?!\_).)*)(?:\b|\_)/ig);

(or)

You can check the non-words \W and _ for the first grouping here, and pushing the matches in the array

@arrs = ($str=~m/number\:([^\W\_]+)(?:\_|\b)/ig);

print the output

print join "\n", @arrs;

__DATA__
icnnumber:9876AB54321_IN
number:987654321FR
icnnumber:987654321YQ
Related