I am just entering to the perl world, I got a task to replace multiple xml file in a folder using perl, I tried some of the perl one line code but it does not helped me out, I need a perl code which replace multiple text files in a selected folder. I tried this below post from stackoverflow Replace values for multiple XML files in a folder using perl but it also not helped me. Kindly be gentle because I am new, I furnish my tried code from above stackflow post showing error, please see and suggest solution.
my $dir = ***D:\Perl***;
my $d = opendir();
map {
if (
-f "$dir/$_"
&& ($_ =~ "\.xml$")
) {
open (my $input_file, '<', ) or die "unable to open $input_file $!\n";
my $input;
{
local $/; #Set record separator to undefined.
$input = <$input_file>; #This allows the whole input file to be read at once.
}
close $input_file;
$input =~ s/Comment//g;
open (my $output_file, '>', "$dir/$_") or die "unable to open $output_file $!\n";
print {$output_file} $input;
close $output_file or die $!;
}
} readdir($d);
closedir($d);
error
syntax error at hello3.pl line 10, near "=~ "\.xml$""
Global symbol "$dir" requires explicit package name at hello3.pl line 23.
Global symbol "$output_file" requires explicit package name at hello3.pl line 23.
syntax error at hello3.pl line 28, near "}"
Global symbol "$d" requires explicit package name at hello3.pl line 28.
Global symbol "$d" requires explicit package name at hello3.pl line 29.
Execution of hello3.pl aborted due to compilation errors.
XML files are in the folder D:\\Perl\\
1.xml
2.xml
3.xml
codes in each xml files are follow below
<?xml version="1.0">
<root>
<!--This is my comment line 1-->
<subtag>
<element>This is 1.xml file</element>
</subtag>
</root>
I'm impressed as a newcomer to Perl, you've latched on to map
. map
is designed to turn an array into a hash - and it can do this by evaluating a code block.
However that's pretty nasty, because it creates code that's hard to follow. Why not instead use a for
(or foreach
) loop? The key warning sign is 'am I assigning the result of map to a hash (or hashref)?' If the answer is no, then chances are this isn't a good way to do it.
Also: I tend to prefer glob
over opendir
for this style of iteration operation.
But most importantly of all:
Please, please, please use an XML Parser to parse XML. Doing so via regular expressions is just nasty - it makes brittle unreliable code. There's a bunch of things in the XML spec that makes semantically identical XML (and therefore 'valid' from a perspective of an upstream system) not match your regular expressions. Things like unary tags, line wrapping and splitting tags across lines.
As an example:
<XML
><some_tag
att1="1"
att2="2"
att3="3"
></some_tag></XML>
Or:
<XML><some_tag att1="1" att2="2" att3="3"></some_tag></XML>
Or:
<XML>
<some_tag
att1="1"
att2="2"
att3="3"></some_tag>
</XML>
Or:
<XML>
<some_tag att1="1" att2="2" att3="3"></some_tag>
</XML>
Or:
<XML>
<some_tag att1="1" att2="2" att3="3"/>
</XML>
All 'say' basically the same thing (technically there's a minor difference between a 'no text' and a 'null text' in the last example), but as I hope you can clearly see - a line and regex based test to encompass all of them would be difficult. Which is why I keep on suggesting - "use a parser" every time this comes up.
With that in mind - you probably don't actually need to remove comments at all - because they're part of the XML spec, and it's far better to handle them as part of the parse process.
I like XML::Twig
and perl
for this. Other modules exist though, and it may be that you get on with others (like XML::LibXML
) instead.
Oh, and there's an error in your XML the line should be:
<?xml version="1.0"?>
Anyway, with that in mind - to answer your question as asked:
#!/usr/local/bin/perl
use strict;
use warnings;
use XML::Twig;
foreach my $file ( glob("$dir/*.xml") ) {
my $twig =
XML::Twig->new( comments => 'drop', pretty_print => 'indented_a' );
$twig->parsefile($file);
open( my $output, ">", $file . ".new" ) or warn $!;
print {$output} $twig->sprint;
close($output);
}
This will turn your sample XML into:
<?xml version="1.0"?>
<root>
<subtag>
<element>This is 1.xml file</element>
</subtag>
</root>
If you were wanting to delete something other than comments - bearing in mind that comments are a special case - and instead wanted to say, get rid of a particular element:
XML::Twig->new( pretty_print => 'indented_a',
twig_handlers => { 'element' => sub { $_ -> delete } } );
Note - this will delete every element tag - you can apply more selective criteria either via an xpath expression (eg 'subtag/element'
) or use a proper subroutine to handle and parse:
sub delete_element_with_file {
my ( $twig, $element ) = @_;
if ( $element->text =~ m/file/ ) { $element->delete }
}
my $twig = XML::Twig->new(
pretty_print => 'indented_a',
twig_handlers => { 'subtag/element' => \&delete_element_with_file }
);
##etc.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.