Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

sequential substitutions

by Anonymous Monk
on Aug 03, 2018 at 09:53 UTC ( [id://1219785]=perlquestion: print w/replies, xml ) Need Help??

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

I can brute force this, but certainly don't know how to do it with elegance and brevity.

Suppose I have an XML fragment like:

<foo>3</foo> <foo>14</foo> <foo>159</foo> [...]

I want to change that data to sequential numbering, ie.:

<foo>1</foo> <foo>2</foo> <foo>3</foo> [...]

Is there a reasonably compact way of doing that (preferably without using additional modules)?

Thank you in advance.

Replies are listed 'Best First'.
Re: sequential substitutions
by haukex (Archbishop) on Aug 03, 2018 at 10:01 UTC

    See Parsing HTML/XML with Regular Expressions for why it's not a good idea to do something like this without modules.

    use warnings; use strict; use Mojo::DOM; my $xml = <<'ENDXML'; <foo>3</foo> <foo>14</foo> <foo>159</foo> ENDXML my $dom = Mojo::DOM->new->xml(1)->parse($xml); my $i = 1; $dom->find('foo')->each(sub{ $_->content($i++) }); print $dom->to_string; __END__ <foo>1</foo> <foo>2</foo> <foo>3</foo>

      I certainly see your point about not trying to parse arbitrary HTML or XML with regular expressions, but in this case, I am looking for a specific tag with numeric values that have already been sanitized by another program.

      That does lead me to a side question, though. Do you know of an HTML "cleanup" program (or module) that will take (mostly arbitrary) HTML and output one tag per line properly indented?

        I certainly see your point about not trying to parse arbitrary HTML or XML with regular expressions, but in this case, I am looking for a specific tag with numeric values that have already been sanitized by another program.

        Can you trust the source of the XML to never change (whitspace, attributes, CDATA, namespaces, comments, etc.), and can you trust the program that's doing the sanitization to never change its output either? What's wrong with installing modules, especially such helpful ones? (Yes, even you can use CPAN.)

        Sure, it's possible to adapt the regexes shown by the others to this purpose to work on the sample data you've showed, but I personally am not going to jump down the rabbit hole of parsing XML with regexes in this case :-)

        Do you know of an HTML "cleanup" program (or module) that will take (mostly arbitrary) HTML and output one tag per line properly indented?

        Searching for "html tidy" on Google and CPAN shows lots of different options (I haven't used any of them, so I can't give you recommendations). Plus, I wonder why you're asking about such a program, if you said above your XML is already sanitized? ;-)

Re: sequential substitutions
by tybalt89 (Monsignor) on Aug 03, 2018 at 10:32 UTC
    #!/usr/bin/perl use strict; use warnings; my $fragment = ' <foo>3</foo> <foo>14</foo> <foo>159</foo> [...] '; print $fragment; my $n = 1; $fragment =~ s/\d+/ $n++ /ge; print $fragment;

      This is pretty much what I was looking for (actually, it is even more than I was looking for - I thought I would at a minimum have to loop over the matches with a while statement :) ). I already have it incorporated into my program, and it is working.

      Thank you very much.

Re: sequential substitutions
by choroba (Cardinal) on Aug 03, 2018 at 16:51 UTC
    Using XML::XSH2, a wrapper around XML::LibXML:
    open file.xml ; $i = 0; for //foo set . {++$i} ; save :b ;
    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: sequential substitutions
by anonymized user 468275 (Curate) on Aug 03, 2018 at 10:08 UTC
    As stated it is simple enough, but there is usually more to the story, requiring more complex parsing than just detecting digits and substituting.
    ( echo '<foo>3</foo>'; echo '<foo>14</foo>'; echo '<foo>159</foo>') | +perl -e ' my $x = 0; while (my $l = <>) { $x++; $l =~ s/\d+/$x/; print $l; }' <foo>1</foo> <foo>2</foo> <foo>3</foo>

    One world, one people

      There is a little bit more to it (but not much), and I do apologize for over-simplifying.

      Let's suppose the code is actually:

      <foo>3</foo> <bar>a</bar> <foo>14</foo> <bar>bc</bar> <foo>159</foo> <bar>def</bar> [...]

      I want to simply echo lines that do not contain the "foo" tag to the output file.

        Like this?

        use strict; use warnings; my $i = 0; while(<DATA>){ s/<foo>.*?<\/foo>/"<foo>".++$i."<\/foo>"/e; print; } __DATA__ <foo>3</foo> <bar>a</bar> <foo>14</foo> <bar>bc</bar> <foo>159</foo> <bar>def</bar>
Re: sequential substitutions
by Anonymous Monk on Aug 04, 2018 at 15:22 UTC
    $text = '<foo>3</foo><foo>14</foo><foo>159</foo>'; while($text =~ /<foo>(.*?)<\/foo>/g) { push (@la,$1); }; sort @la; foreach(@la) { print "<foo>$_<\/foo>\n"; }

      Sorry, but there are at least two things wrong with that code (aside from using regexes to parse HTML...): It does not re-number the entries as the OP specified, and sort @la; does nothing (should be @la = sort @la; instead, warnings would have told you about this). So really the only effect of this code is to remove any non-<foo> tags from the input and re-format it slightly.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1219785]
Approved by marto
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others sharing their wisdom with the Monastery: (5)
As of 2024-04-19 12:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found