Perl: extracting data from text using regex

I am using Perl to process text with a regular expression. I have no control over the input. I have provided some input examples below.

As you can see, elements B and C can appear n times with different values ​​in a string. I need to get all values ​​as a backlink. Or, if you know otherwise, I am all ears.

I am trying to use the reset branch pattern (as stated in perldoc: "Advanced Templates" ) I have no luck matching string.

("Data" (Int "A" 22) (Int "B" 1) (Int "C" 2) (Int "D" 34896) (Int "E" 38046))
("Data" (Int "A" 22) (Int "B" 1) (Int "C" 2) (Int "B" 3) (Int "C" 4) (Int "B" 5) (Int "C "6) (Int" D "34896) (Int" E "38046))
("Data" (Int "A" 22) (Int "B" 22) (Int "C" 59) (Int "B" 1143) (Int "C" 1210) (Int "B" 1232) (Int "C "34896) (Int" D "34896) (Int" E "38046))

See my Perl below, any help would be great. Thanks for any help you can give.

if($inputString =~/\("Data" \(Int "A" ([0-9]+)\)(?:\(Int "B" ([0-9]+)\)\(Int "C" ([0-9]+)\))+\(Int "D" ([0-9]+)\)\(Int "E" ([0-9]+)\)\)/) {

    print "\n\nmatched\n";

    print "1: $1\n";
    print "2: $2\n";
    print "3: $3\n";
    print "4: $4\n";
    print "5: $5\n";
    print "6: $6\n";
    print "7: $7\n";
    print "8: $8\n";
    print "9: $9\n";

}

      

+1


a source to share


3 answers


Don't try to use a single regex, the set of regex and sections are easier to understand:

#!/usr/bin/perl

use strict;
use warnings;

while (<DATA>) {
    next unless my ($data) = /\("Data" (.*)\)/;
    print "on line $., I saw:\n";
    for my $item ($data =~ /\((.*?)\)/g) {
        my ($type, $var, $num) = split " ", $item;
        print "\ttype $type var $var num $num\n";
    }
}

__DATA__
("Data" (Int "A" 22)(Int "B" 1)(Int "C" 2)(Int "D" 34896)(Int "E" 38046))
("Data" (Int "A" 22)(Int "B" 1)(Int "C" 2)(Int "B" 3)(Int "C" 4)(Int "B" 5)(Int "C" 6)(Int "D" 34896)(Int "E" 38046))
("Data" (Int "A" 22)(Int "B" 22)(Int "C" 59)(Int "B" 1143)(Int "C" 1210)(Int "B" 1232)(Int "C" 34896)(Int "D" 34896)(Int "E" 38046))

      



If your data can stretch across lines, I would suggest using a parser instead of regex.

+10


a source


I'm not sure what the use of getting the values ​​as backlinks - who would you like to deal with the duplicate keys case (eg "C" in the second line). Also I'm not sure what you want to do with the values ​​after retrieving.

But I would start with something like:



use Data::Dumper;

while (<DATA>)
{
    my @a = m!\(Int "(.*?)" ([0-9]+)\)!g;
    print Dumper(\@a);
}

__DATA__
("Data" (Int "A" 22)(Int "B" 1)(Int "C" 2)(Int "D" 34896)(Int "E" 38046))
("Data" (Int "A" 22)(Int "B" 1)(Int "C" 2)(Int "B" 3)(Int "C" 4)(Int "B" 5)(Int "C"     6)(Int "D" 34896)(Int "E" 38046)) 
("Data" (Int "A" 22)(Int "B" 22)(Int "C" 59)(Int "B" 1143)(Int "C" 1210)(Int "B" 1232)(Int "C" 34896)(Int "D" 34896)(Int "E" 38046))

      

This gives you an array of duplicate keys, values.

+3


a source


My initial thought was to use named captures and to get values ​​from %-

:

my $pattern = qr/
  \(
    "Data"\s+
    \(Int\s+"A"\s+(?<A>[0-9]+)\)
    (?:
      \(Int\s+"B"\s+(?<B>[0-9]+)\)
      \(Int\s+"C"\s+(?<C>[0-9]+)\)
    )+
    \(Int\s+"D"\s+(?<D>[0-9]+)\)
    \(Int\s+"E"\s+(?<E>[0-9]+)\)
  \)
/x;

      

Unfortunately grouping (?:...)

does not trigger multiple values ​​for B and C. I suspect this is a bug. Doing this explicitly captures all values, but you will need to know the maximum number of instances ahead of time.

my $pattern = qr/
  \(
    "Data"\s+
    \(Int\s+"A"\s+(?<A>[0-9]+)\)
    \(Int\s+"B"\s+(?<B>[0-9]+)\)
    \(Int\s+"C"\s+(?<C>[0-9]+)\)
    (?:
      \(Int\s+"B"\s+(?<B>[0-9]+)\)
      \(Int\s+"C"\s+(?<C>[0-9]+)\)
    )?
    (?:
      \(Int\s+"B"\s+(?<B>[0-9]+)\)
      \(Int\s+"C"\s+(?<C>[0-9]+)\)
    )?
    # repeat (?:...) N times
    \(Int\s+"D"\s+(?<D>[0-9]+)\)
    \(Int\s+"E"\s+(?<E>[0-9]+)\)
  \)
/x;

      

The easiest way is to use m//g

. You can either capture name / value pairs, as Beano suggests, or use multiple templates to capture each value:

my @b = m/Int "B" ([0-9]+)/g;
my @c = m/Int "C" ([0-9]+)/g;
# etc.

      

+1


a source







All Articles