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

I am trying to append user form info (ie. name, address) to an xml file between two xml open & close markers: <markers>... </markers>. Rather than appending with the standard Open >> which writes to EOF, I want to append newly submitted user form data before the closing </marker>.

Is there a quick and dirty way to alter my print routine below to always append a record just before the closing </markers>

I have tried to add a Print if statement but with not much luck! Any suggestions greatly appreciated!

Many thanks for any help in advance!

Printing routine follows below:

open (DATABASE, ">>$location_of_database") || &file_open_error("$location_of_database", "Append a Database" +, _FILE__, __LINE__); #Add new 'print if' code below to print new record before </markers +> print DATABASE "<marker $database_row />\n"; close (DATABASE); &release_file_lock ("$location_of_database.lock"); } # End of if (-w $location_of_database) # If the database file was not writable, the script # sends an error message back to the user. else { &cannot_find_database; exit; } } # End of if ($should_I_append_a_database eq "yes")

20070510 Janitored by Corion: Added formatting, code tags, as per Writeup Formatting Tips

Replies are listed 'Best First'.
Re: Appending a file between XML markers
by Joost (Canon) on May 09, 2007 at 11:07 UTC
    It's much easier to open the original file for reading, and write a complete new file to another location, and if that all succeeds rename() the new file over the old file:

    open INPUT,"<","input.xml" or die $!; open OUTPUT,">","output.xml.tmp" or die $!; while (<INPUT>) { s/<\/markers>/$database_row<\/markers>/; # insert $database row bef +ore marker, if found on this line print OUTPUT $_; # print line to output file } close INPUT; close OUTPUT or die $!; rename "output.xml.tmp","input.xml";
    If you're doing anything more interesting than your stated problem, it'll be worth it to use a real XML parser instead. I recommend XML::Twig.

Re: Appending a file between XML markers
by Jenda (Abbot) on May 09, 2007 at 13:32 UTC

    If I understand things right you just need to overcome the stupid decision of the XML specification authors to require that a XML file is all onclosed in a single tag effectively preventing appending to XML files, right?

    One solution would be to leave of the closing marker from the file as it's being appended to and only add it whenever you need to read it. The other would be to open the file in <+ mode, seek to a position a few bytes before the end and write the data and the closing tag.

    my $marker = "<marker>\n"; my $endmarker = "</marker>\n"; my $marker_length = length($endmarker); $marker_length++ if $^O =~ /MSWin/; # because of the \n -> CRLF conver +sion sub append { my ($file, $data) = @_; if (!-s $file) { open my $FH, '>', $file or die qq{Can't open "$file" : $^E\n}; print $FH $marker, $data, $endmarker; close $FH; } else { open my $FH, '+<', $file or die qq{Can't open "$file" : $^E\n} +; seek $FH, -$marker_length, 2; print $FH $data, $endmarker; close $FH; } } append('text.xml', "<foo>ahoj</foo>\n"); append('text.xml', "<foo>cau</foo>\n"); append('text.xml', "<foo>co</foo>\n"); append('text.xml', "<foo>delas?</foo>\n");

    I did not include any locking so that it doesn't obscure the solution. You should definitely add it.

Re: Appending a file between XML markers
by TOD (Friar) on May 09, 2007 at 11:00 UTC
    may be a look at Tie::Scalar or one of its relatives will give you a hint.
    --------------------------------
    masses are the opiate for religion.