0

I need to substitute the following string:

<book name=""
      author="">

to

<magazine>

my code below does not work:

sub substitution
{
my $find = "book name\=\"\"\nauthor\=\"\"";
my $replace = "magazine";
{  
local @ARGV = ("$_[0]");
local $^I = '.bac';

while( <> )
{
  if( s/$find/$replace/ig ) {
     print;
                            }

else {
     print;
     }
} //while
}
innaM
  • 47,505
  • 4
  • 67
  • 87
laurentngu
  • 357
  • 2
  • 13

4 Answers4

3

Like Brian already said, use an XML parser. Here's a sample using XML::LibXML and doing the DOM manipulation with setNodeName (for changing the element name) and removeAttributes (for removing the two attributes):

use strict;
use XML::LibXML;

my $doc = XML::LibXML->new->parse_string(<<EOF);
<books>
 <book name=""
       author="">
  <chapter>something</chapter>
 </book>
 <book name=""
       author="">
  <chapter>something</chapter>
 </book>
</books>
EOF

for my $book_node ($doc->findnodes('//book')) {
    $book_node->setNodeName('magazine');
    $book_node->removeAttribute($_) for qw(name author);
}

print $doc->serialize;

Another possibility would be to use xslt here...

Slaven Rezic
  • 4,571
  • 14
  • 12
1

I would strongly recommend you use an XML parser rather than regexps, for well-documented reasons.

Check out the Comprehensive Perl Archive Network for more suitable libraries.

Community
  • 1
  • 1
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • You *can* use Perl. Perl has a huge number of libraries to help with almost everything, including the above (see link) – Brian Agnew Jul 10 '13 at 08:57
  • my code is working fine. But the carriage return between "book name" and "author" makes my code fail. There is a slight modification to do on my code, but I can not find it – laurentngu Jul 10 '13 at 09:03
  • 1
    @laurentngu: You are *strongly advised* to take Brian's advice. You shouldn't process XML using regexes. – Borodin Jul 10 '13 at 10:30
1

If you want quick and easy solution (why else would you want to use Perl?), then just use

my $find = qr|<book name=""\s+author="">|s;
my $replace = '<magazine>';

And as you want to replace something across multiple lines, you cannot read line by line, instead you should slurp file in scalar (if your file is small enough to fit into memory)

local $/; # undefines input lines separator
# open your file with open(FILE, '<', $filename);
my $text = <FILE>;
$text =~ s/$find/$replace/g;
# do with $text what you want now, print it or anything
# don't forget to close your FILE

This is quick and dirty, but works well. If your file does not fit into memory, or you want to be sure that everything works ok, use XML parsers, but remember

  1. do not use XML::Simple, it is broken, really
  2. for large files you need stream XML parsers, like XML::Parser
1

Using xsh, a wrapper around XML::LibXML:

open file.xml ;
for //book[@name="" and @author=""] {
    rename magazine . ;
    delete @* ;
}
save :b ;
choroba
  • 231,213
  • 25
  • 204
  • 289