in reply to Trouble capturing multiple groupings in regex

Thanks everyone. As expected.. the actual data I got to look at is formatted differently than the sample data I'm looking at, and unless I can find where they store these objects in that other format, I'm going to have to go the route of properly parsing XML as people suggested. I was looking at a 'quick' method of just pulling out a mostly accurate listing of variables being used in each of these templates to spot check for consistency in naming and usage, and I may have overestimated my ability to 'quickly do this' considering the documents I'm actually working with are not a match for the samples I had, and not entirely understood at this point. Really do appreciate the help, and this still might come in handy when I can find the appropriately formatted documents, but if I want to build this report I'm probably spending a bit more time and parsing the XML after I can understand how it's built better.

  • Comment on Re: Trouble capturing multiple groupings in regex

Replies are listed 'Best First'.
Re^2: Trouble capturing multiple groupings in regex
by kcott (Archbishop) on Dec 09, 2015 at 20:56 UTC

    Using an XML parser is generally a fairly simple task. Consider this code which extracts the data as you've described:

    #!/usr/bin/env perl -l use strict; use warnings; use XML::LibXML; my $xml_file = 'pm_1149767_xml_parse.xml'; my $parser = XML::LibXML::->new(); my $doc = $parser->load_xml(location => $xml_file); my $re = qr{%([^%]+)%}; for ($doc->findnodes('//span/text()')) { print $1 while /$re/g; }

    Opening and reading a file line-by-line is probably an equivalent amount of code. However, that doesn't take into account <span> elements spread over multiple lines. You show an ideal situation of:

    <span ...>%var%</span>

    However, what about the equally valid XML:

    <span ...> %var% </span>

    The XML parser already has the code to do this. There's little point in attempting to reinvent this wheel; in fact, your chances of getting it completely right (before you've pulled out all of your hair) are small to none.

    I've indicated 'pm_1149767_xml_parse.xml' in the code above. That's an XML file I've dummied up which contains your <span> elements at different levels of the XML hierarchy as well as a number of edge cases. Here it is:

    <root> <A> <span color="#231f20" whatever="%DoNotMatch%" textOverprint="f +alse">%PN1%</span> <span color="#231f20" whatever="%DoNotMatch%" textOverprint="f +alse"> %PN2% </span> </A> <B> <C> <span color="#231f20" textOverprint="false">%DIMMM%%DIMINC +H%</span> <span color="#231f20" textOverprint="false"> %DIMMM% %DIMINCH% </span> <span color="#231f20" textOverprint="false">%DIMMM%garbage +%DIMINCH%</span> <span color="#231f20" textOverprint="false">%DIMMM%%%DIMIN +CH%</span> <span color="#231f20" textOverprint="false">%DIMMM%%%%DIMI +NCH%</span> </C> </B> </root>

    Here's the output from the script I've shown:

    PN1 PN2 DIMMM DIMINCH DIMMM DIMINCH DIMMM DIMINCH DIMMM DIMINCH DIMMM DIMINCH

    It's possible you'll need more information than that for your report. In the spoiler below, you'll find a more involved for loop and more verbose output.

    With this for loop:

    for my $context ($doc->findnodes('//span')) { print $context; for my $text ($context->findnodes('text()')) { print $text; while ($text =~ /$re/g) { print $1; } } }

    You'll get this output:

    <span color="#231f20" whatever="%DoNotMatch%" textOverprint="false">%P +N1%</span> %PN1% PN1 <span color="#231f20" whatever="%DoNotMatch%" textOverprint="false"> %PN2% </span> %PN2% PN2 <span color="#231f20" textOverprint="false">%DIMMM%%DIMINCH%</span> %DIMMM%%DIMINCH% DIMMM DIMINCH <span color="#231f20" textOverprint="false"> %DIMMM% %DIMINCH% </span> %DIMMM% %DIMINCH% DIMMM DIMINCH <span color="#231f20" textOverprint="false">%DIMMM%garbage%DIMINCH%</s +pan> %DIMMM%garbage%DIMINCH% DIMMM DIMINCH <span color="#231f20" textOverprint="false">%DIMMM%%%DIMINCH%</span> %DIMMM%%%DIMINCH% DIMMM DIMINCH <span color="#231f20" textOverprint="false">%DIMMM%%%%DIMINCH%</span> %DIMMM%%%%DIMINCH% DIMMM DIMINCH

    The XML parser I've used is XML::LibXML. I like this one because it's both handy for small demo scripts, such as I have here, and also suited to full-blown, commercial applications, where I've used it often. There's lots of others available on CPAN: pick one that suits you.

    You'll probably also want to look at "XML Path Language (XPath) 3.1". That's a lengthy, W3C specification: I rarely need to reference more than the "3.3.5 Abbreviated Syntax" section.

    — Ken