NAME File::Edit::Portable - Read and write files while keeping the original line-endings intact, no matter the platform. SYNOPSIS use File::Edit::Portable; my $rw = File::Edit::Portable->new; # read a file, replacing original file's line endings with # that of the local platform's default my $fh = $rw->read(file => 'file.txt'); # or my @contents = $rw->read(file => 'file.txt'); # write out a file using original file's record separator $rw->write(contents => \@contents); # replace original file's record separator with a new one $rw->write(recsep => "\r\n", contents => \@contents); # rewrite all files in a directory recursively with local # platform's default record separator $rw->dir(dir => '/path/to/files'); # insert new data into a file after a specified line number $rw->splice(file => $file, line => $num, insert => \@contents); # insert new data into a file after a found search term $rw->splice(file => $file, find => 'term', insert => \@contents); There's also a minimal non-OO interface... use File::Edit::Portable qw(read write); my $fh = read('file.txt'); # and/or my @contents = read('file.txt'); # then write('file.txt', \@contents); DESCRIPTION The default behaviour of perl is to read and write files using the Operating System's (OS) default record separator (line ending). If you open a file on an OS where the record separators are that of another OS, things can and do break. This module will read in a file, keep track of the file's current record separators regardless of the OS. It can return either a file handle (in scalar context) that has had its line endings replaced with that of the local OS platform, or an array of the file's contents (in list context) with line endings stripped off. You can then modify this array and send it back in for writing to the same file or a new file, where the original file's line endings will be re-appended (or a custom ending if you so choose). Uses are for dynamically reading/writing files while on one Operating System, but you don't know whether the record separators are platform-standard. Shared storage between multpile platforms are a good use case. This module affords you the ability to not have to check each file, and is very useful in looping over a directory where various files may have been written by different platforms. EXPORT None by default. See EXPORT_OK EXPORT_OK If you desire using the non-OO functionality, the following functions are exported on demand. read() and write(). If there are namespace collisions with those two functions, pread() and pwrite() are available as well. METHODS new Returns a new File::Edit::Portable object. read Parameters: file => 'filename' In scalar context, will return a read-only file handle to a copy of the file that has had its line endings replaced with those of the local OS platform's record separator. In list context, will return an array, where each element is a line from the file, with all line endings stripped off. In both cases, we save the line endings that were found in the original file (which is used when write() is used, by default). write Writes the data back to the original file, or alternately a copy of the file. Returns 1 on success. If you inadvertantly append newlines to the new elements of the contents array, we'll strip them off before appending the real newlines. Parameters: file => 'file': Not needed if you've used read() to open the file. copy => 'file2': Set this if you want to write to an alternate (new) file, rather than the original. contents => \@contents: Mandatory, should contain a reference to the array that was returned by read(). recsep => "\r\n": Optional, a double-quoted string of any characters you want to write as the line ending (record separator). This value will override what was found in the read() call. Common ones are "\r\n" for Windows, "\n" for Unix and "\r" for Mac. splice Inserts new data into a file after a specified line number or search term. Parameters: file => 'file.name': Mandatory. insert => \@contents: Mandatory - an array reference containing the contents to merge into the file. copy => 'newfile.name': Optional - we'll read from file, but we'll write to this new file. line => 23: Optional - Merge the contents on the line following the one specified here. find => 'search term': Optional - Merge the contents into the file on the line following the first find of the search term. The search term is put into qr, so single quotes are recommended, and all regex patterns are honoured. NOTE: Although both are optional, at least one of line or find must be sent in. Returns an array of the modified file contents. dir Rewrites the line endings in some or all files within a directory structure recursively. Returns an array of the names of the files found. Parameters: dir => '/path/to/files': Mandatory. types => ['*.txt', '*.dat']: Optional. Specify wildcard combinations for files to work on. We'll accept anything that File::Find::Rule's name() method does. If not supplied, we work on all files. maxdepth => 1: Optional: Specify how many levels of recursion to do after entering the directory. We'll do a full recurse through all sub-directories if this parameter is not set. recsep => "\r\n": Optional: If this parameter is not sent in, we'll replace the line endings with that of the current platform we're operating on. Otherwise, we'll use the double-quoted value sent in. list => 1 If set, we'll return an array of the names of the files found, but won't take any editing action on them. Default is disabled. recsep('file') Returns a string of the hex representation of the line endings (record separators) in 'file'. For example, "\0d\0a" will be returned for Windows line endings (CRLF). If an empty file is being checked, we'll return the local platform's record separator. platform_recsep Returns the string representation of the current platform's (OS) record separator. Takes no parameters. FUNCTIONS read('file.txt') pread() can alternately be imported in the event of namespace collisions. In scalar context, will return a read-only file handle. In list context, returns an array with each element being a line in the file, with the endings stripped off. write('file.txt', \@contents, 'copy.txt', "\r\n") pwrite() can alternately be imported in the event of namespace collisions. Writes back out the file (or alternately a new file (copy.txt), using the original file's line endings, or optionally a custom record separator as specified by the last parameter. Note the record separator MUST be sent in within double-quotes. If you want to send in a custom record separator but not use a copy file, just set the third parameter (copy.txt) to undef within the call. AUTHOR Steve Bertrand, BUGS Please report any bugs or feature requests to bug-file-edit-portable at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=File-Edit-Portable. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. REPOSITORY https://github.com/stevieb9/file-edit-portable BUILD RESULTS Travis-CI: https://travis-ci.org/stevieb9/file-edit-portable CPAN Testers: http://matrix.cpantesters.org/?dist=File-Edit-Portable SUPPORT You can find documentation for this module with the perldoc command. perldoc File::Edit::Portable You can also look for information at: * RT: CPAN's request tracker (report bugs here) http://rt.cpan.org/NoAuth/Bugs.html?Dist=File-Edit-Portable * Search CPAN http://search.cpan.org/dist/File-Edit-Portable/ LICENSE AND COPYRIGHT Copyright 2015 Steve Bertrand. This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License. See http://dev.perl.org/licenses/ for more information.