In my day job, amongst other things, I write training materials and run courses for Perl Training Australia. We've only recently added File::Temp to our course notes, and since we allow our attendees to use either our portable training server (Linux) or their desktop (often Windows) we've hit a problem.
File::Temp's tempfile isn't portable for text files. This is because it opens them using sysopen and the O_BINARY flag (where available). This means that newlines printed to the file aren't converted into the operating system's preferred end of line character(s).
This shouldn't have been a surprise, it's in the documentation:
BINMODE
The file returned by File::Temp will have been opened in binary mode if such a mode is available. If that is not correct, use the binmode() function to change the mode of the filehandle.
Note that you can modify the encoding of a file opened by File::Temp also by using binmode().
However the binmode documentation rightly points out:
For the sake of portability it is a good idea to always use it when appropriate, and to never use it when it isn't appropriate.
I know I can tell the Windows students to write:
binmode($tmp_fh, ":crlf" );
after their call to tempfile(), but it's still not going to be portable. Creating a tempfile doesn't look like it should be a special case, and in my experience, it's usually safe to tell (fairly computer and operating system savvy) students that if it looks portable, it should be.
Is there a layer I can give to binmode to tell it to go back to treating the file as a text file with all the special magic regarding newlines that should happen? I essentially want a: binmode($tmp_fh, ":default");
I can always write File::Temp::Text which doesn't use O_BINARY but that's another, non-standard, module for the students to have to install.
I originally brought this question up on PerlMonks but I'm still hoping for a magic answer that lets me teach students a portable way of handling temporary text files where the file name is available. (Yes, I know about open with an undef filehandle).
Any hints?
Thanks, jarich
Re:Do you need portable code? Or portable tempfile
jarich on 2008-02-26T22:41:39
One of my common uses of temporary files is to allow me to make changes to a whole file, know that that all succeeded and then replace said file. For example, to reverse each line in a file I might write:
# Reverses each line in a file
use File::Temp qw(tempfile);
use File::Copy qw(move);
use Fatal qw(open close move);
# Open files
my $filename = shift or die "Usage: $0 filename";
open(my $in, "<", $filename);
my ($tmp_fh, $tmp_name) = tempfile();
# Read line in, remove newline, reverse and print it
while(<$in>) {
chomp;
print {$tmp_fh} scalar(reverse($_)), "\n";
}
# Close files. If no errors here, then everything succeeded!
close $in;
close $tmp_fh;
# Move temp-file over original
move($tmp_name, $filename);This is a case of using a temporary file - temporarily - but wanting to keep the result permanently. Sure it's a contrived example (one of the exercises from our class in fact), but it should show why not treating \n correctly becomes a problem in the end result.