📄 perlfaq5.pod
字号:
=head1 NAMEperlfaq5 - Files and Formats ($Revision: 10126 $)=head1 DESCRIPTIONThis section deals with I/O and the "f" issues: filehandles, flushing,formats, and footers.=head2 How do I flush/unbuffer an output filehandle? Why must I do this?X<flush> X<buffer> X<unbuffer> X<autoflush>Perl does not support truly unbuffered output (except insofar as youcan C<syswrite(OUT, $char, 1)>), although it does support is "commandbuffering", in which a physical write is performed after every outputcommand.The C standard I/O library (stdio) normally buffers characters sent todevices so that there isn't a system call for each byte. In most stdioimplementations, the type of output buffering and the size of thebuffer varies according to the type of device. Perl's C<print()> andC<write()> functions normally buffer output, while C<syswrite()>bypasses buffering all together.If you want your output to be sent immediately when you executeC<print()> or C<write()> (for instance, for some network protocols),you must set the handle's autoflush flag. This flag is the Perlvariable C<$|> and when it is set to a true value, Perl will flush thehandle's buffer after each C<print()> or C<write()>. Setting C<$|>affects buffering only for the currently selected default filehandle.You choose this handle with the one argument C<select()> call (seeL<perlvar/$E<verbar>> and L<perlfunc/select>).Use C<select()> to choose the desired handle, then set itsper-filehandle variables. $old_fh = select(OUTPUT_HANDLE); $| = 1; select($old_fh);Some modules offer object-oriented access to handles and theirvariables, although they may be overkill if this is the only thing youdo with them. You can use C<IO::Handle>: use IO::Handle; open my( $printer ), ">", "/dev/printer"); # but is this? $printer->autoflush(1);or C<IO::Socket> (which inherits from C<IO::Handle>): use IO::Socket; # this one is kinda a pipe? my $sock = IO::Socket::INET->new( 'www.example.com:80' ); $sock->autoflush();You can also flush an C<IO::Handle> object without settingC<autoflush>. Call the C<flush> method to flush the buffer yourself: use IO::Handle; open my( $printer ), ">", "/dev/printer"); $printer->flush; # one time flush =head2 How do I change, delete, or insert a line in a file, or append to the beginning of a file?X<file, editing>(contributed by brian d foy)The basic idea of inserting, changing, or deleting a line from a textfile involves reading and printing the file to the point you want tomake the change, making the change, then reading and printing the restof the file. Perl doesn't provide random access to lines (especiallysince the record input separator, C<$/>, is mutable), although modulessuch as C<Tie::File> can fake it.A Perl program to do these tasks takes the basic form of opening afile, printing its lines, then closing the file: open my $in, '<', $file or die "Can't read old file: $!"; open my $out, '>', "$file.new" or die "Can't write new file: $!"; while( <$in> ) { print $out $_; } close $out;Within that basic form, add the parts that you need to insert, change,or delete lines.To prepend lines to the beginning, print those lines before you enterthe loop that prints the existing lines. open my $in, '<', $file or die "Can't read old file: $!"; open my $out, '>', "$file.new" or die "Can't write new file: $!"; print "# Add this line to the top\n"; # <--- HERE'S THE MAGIC while( <$in> ) { print $out $_; } close $out;To change existing lines, insert the code to modify the lines insidethe C<while> loop. In this case, the code finds all lowercasedversions of "perl" and uppercases them. The happens for every line, sobe sure that you're supposed to do that on every line! open my $in, '<', $file or die "Can't read old file: $!"; open my $out, '>', "$file.new" or die "Can't write new file: $!"; print "# Add this line to the top\n"; while( <$in> ) { s/\b(perl)\b/Perl/g; print $out $_; } close $out;To change only a particular line, the input line number, C<$.>, isuseful. First read and print the lines up to the one you want tochange. Next, read the single line you want to change, change it, andprint it. After that, read the rest of the lines and print those: while( <$in> ) # print the lines before the change { print $out $_; last if $. == 4; # line number before change } my $line = <$in>; $line =~ s/\b(perl)\b/Perl/g; print $out $line; while( <$in> ) # print the rest of the lines { print $out $_; } To skip lines, use the looping controls. The C<next> in this exampleskips comment lines, and the C<last> stops all processing once itencounters either C<__END__> or C<__DATA__>. while( <$in> ) { next if /^\s+#/; # skip comment lines last if /^__(END|DATA)__$/; # stop at end of code marker print $out $_; }Do the same sort of thing to delete a particular line by using C<next>to skip the lines you don't want to show up in the output. Thisexample skips every fifth line: while( <$in> ) { next unless $. % 5; print $out $_; }If, for some odd reason, you really want to see the whole file at oncerather than processing line by line, you can slurp it in (as long asyou can fit the whole thing in memory!): open my $in, '<', $file or die "Can't read old file: $!" open my $out, '>', "$file.new" or die "Can't write new file: $!"; my @lines = do { local $/; <$in> }; # slurp! # do your magic here print $out @lines;Modules such as C<File::Slurp> and C<Tie::File> can help with thattoo. If you can, however, avoid reading the entire file at once. Perlwon't give that memory back to the operating system until the processfinishes.You can also use Perl one-liners to modify a file in-place. Thefollowing changes all 'Fred' to 'Barney' in F<inFile.txt>, overwritingthe file with the new contents. With the C<-p> switch, Perl wraps aC<while> loop around the code you specify with C<-e>, and C<-i> turnson in-place editing. The current line is in C<$_>. With C<-p>, Perlautomatically prints the value of C<$_> at the end of the loop. SeeL<perlrun> for more details. perl -pi -e 's/Fred/Barney/' inFile.txtTo make a backup of C<inFile.txt>, give C<-i> a file extension to add: perl -pi.bak -e 's/Fred/Barney/' inFile.txtTo change only the fifth line, you can add a test checking C<$.>, theinput line number, then only perform the operation when the testpasses: perl -pi -e 's/Fred/Barney/ if $. == 5' inFile.txtTo add lines before a certain line, you can add a line (or lines!)before Perl prints C<$_>: perl -pi -e 'print "Put before third line\n" if $. == 3' inFile.txtYou can even add a line to the beginning of a file, since the currentline prints at the end of the loop: perl -pi -e 'print "Put before first line\n" if $. == 1' inFile.txtTo insert a line after one already in the file, use the C<-n> switch.It's just like C<-p> except that it doesn't print C<$_> at the end ofthe loop, so you have to do that yourself. In this case, print C<$_>first, then print the line that you want to add. perl -ni -e 'print; print "Put after fifth line\n" if $. == 5' inFile.txtTo delete lines, only print the ones that you want. perl -ni -e 'print unless /d/' inFile.txt ... or ... perl -pi -e 'next unless /d/' inFile.txt=head2 How do I count the number of lines in a file?X<file, counting lines> X<lines> X<line>One fairly efficient way is to count newlines in the file. Thefollowing program uses a feature of tr///, as documented in L<perlop>.If your text file doesn't end with a newline, then it's not really aproper text file, so this may report one fewer line than you expect. $lines = 0; open(FILE, $filename) or die "Can't open `$filename': $!"; while (sysread FILE, $buffer, 4096) { $lines += ($buffer =~ tr/\n//); } close FILE;This assumes no funny games with newline translations.=head2 How can I use Perl's C<-i> option from within a program?X<-i> X<in-place>C<-i> sets the value of Perl's C<$^I> variable, which in turn affectsthe behavior of C<< <> >>; see L<perlrun> for more details. Bymodifying the appropriate variables directly, you can get the samebehavior within a larger program. For example: # ... { local($^I, @ARGV) = ('.orig', glob("*.c")); while (<>) { if ($. == 1) { print "This line should appear at the top of each file\n"; } s/\b(p)earl\b/${1}erl/i; # Correct typos, preserving case print; close ARGV if eof; # Reset $. } } # $^I and @ARGV return to their old values hereThis block modifies all the C<.c> files in the current directory,leaving a backup of the original data from each file in a newC<.c.orig> file.=head2 How can I copy a file?X<copy> X<file, copy>(contributed by brian d foy)Use the File::Copy module. It comes with Perl and can do atrue copy across file systems, and it does its magic ina portable fashion. use File::Copy; copy( $original, $new_copy ) or die "Copy failed: $!";If you can't use File::Copy, you'll have to do the work yourself:open the original file, open the destination file, then printto the destination file as you read the original.=head2 How do I make a temporary file name?X<file, temporary>If you don't need to know the name of the file, you can use C<open()>with C<undef> in place of the file name. The C<open()> functioncreates an anonymous temporary file. open my $tmp, '+>', undef or die $!;Otherwise, you can use the File::Temp module. use File::Temp qw/ tempfile tempdir /; $dir = tempdir( CLEANUP => 1 ); ($fh, $filename) = tempfile( DIR => $dir ); # or if you don't need to know the filename $fh = tempfile( DIR => $dir );The File::Temp has been a standard module since Perl 5.6.1. If youdon't have a modern enough Perl installed, use the C<new_tmpfile>class method from the IO::File module to get a filehandle opened forreading and writing. Use it if you don't need to know the file's name: use IO::File; $fh = IO::File->new_tmpfile() or die "Unable to make new temporary file: $!";If you're committed to creating a temporary file by hand, use theprocess ID and/or the current time-value. If you need to have manytemporary files in one process, use a counter: BEGIN { use Fcntl; my $temp_dir = -d '/tmp' ? '/tmp' : $ENV{TMPDIR} || $ENV{TEMP}; my $base_name = sprintf "%s/%d-%d-0000", $temp_dir, $$, time; sub temp_file { local *FH; my $count = 0; until( defined(fileno(FH)) || $count++ > 100 ) { $base_name =~ s/-(\d+)$/"-" . (1 + $1)/e; # O_EXCL is required for security reasons. sysopen FH, $base_name, O_WRONLY|O_EXCL|O_CREAT; } if( defined fileno(FH) ) { return (*FH, $base_name); } else { return (); } } }=head2 How can I manipulate fixed-record-length files?X<fixed-length> X<file, fixed-length records>The most efficient way is using L<pack()|perlfunc/"pack"> andL<unpack()|perlfunc/"unpack">. This is faster than usingL<substr()|perlfunc/"substr"> when taking many, many strings. It isslower for just a few.Here is a sample chunk of code to break up and put back together againsome fixed-format input lines, in this case from the output of a normal,Berkeley-style ps: # sample input line: # 15158 p5 T 0:00 perl /home/tchrist/scripts/now-what my $PS_T = 'A6 A4 A7 A5 A*'; open my $ps, '-|', 'ps'; print scalar <$ps>; my @fields = qw( pid tt stat time command ); while (<$ps>) { my %process; @process{@fields} = unpack($PS_T, $_); for my $field ( @fields ) { print "$field: <$process{$field}>\n"; } print 'line=', pack($PS_T, @process{@fields} ), "\n"; }We've used a hash slice in order to easily handle the fields of each row.Storing the keys in an array means it's easy to operate on them as agroup or loop over them with for. It also avoids polluting the programwith global variables and using symbolic references.=head2 How can I make a filehandle local to a subroutine? How do I pass filehandles between subroutines? How do I make an array of filehandles?X<filehandle, local> X<filehandle, passing> X<filehandle, reference>As of perl5.6, open() autovivifies file and directory handlesas references if you pass it an uninitialized scalar variable.You can then pass these references just like any other scalar,and use them in the place of named handles. open my $fh, $file_name; open local $fh, $file_name; print $fh "Hello World!\n"; process_file( $fh );If you like, you can store these filehandles in an array or a hash.If you access them directly, they aren't simple scalars and youneed to give C<print> a little help by placing the filehandlereference in braces. Perl can only figure it out on its own whenthe filehandle reference is a simple scalar. my @fhs = ( $fh1, $fh2, $fh3 ); for( $i = 0; $i <= $#fhs; $i++ ) { print {$fhs[$i]} "just another Perl answer, \n"; }Before perl5.6, you had to deal with various typeglob idiomswhich you may see in older code. open FILE, "> $filename"; process_typeglob( *FILE ); process_reference( \*FILE ); sub process_typeglob { local *FH = shift; print FH "Typeglob!" } sub process_reference { local $fh = shift; print $fh "Reference!" }If you want to create many anonymous handles, you shouldcheck out the Symbol or IO::Handle modules.=head2 How can I use a filehandle indirectly?X<filehandle, indirect>An indirect filehandle is using something other than a symbolin a place that a filehandle is expected. Here are waysto get indirect filehandles: $fh = SOME_FH; # bareword is strict-subs hostile $fh = "SOME_FH"; # strict-refs hostile; same package only $fh = *SOME_FH; # typeglob $fh = \*SOME_FH; # ref to typeglob (bless-able) $fh = *SOME_FH{IO}; # blessed IO::Handle from *SOME_FH typeglobOr, you can use the C<new> method from one of the IO::* modules tocreate an anonymous filehandle, store that in a scalar variable,and use it as though it were a normal filehandle. use IO::Handle; # 5.004 or higher $fh = IO::Handle->new();Then use any of those as you would a normal filehandle. Anywhere thatPerl is expecting a filehandle, an indirect filehandle may be usedinstead. An indirect filehandle is just a scalar variable that containsa filehandle. Functions like C<print>, C<open>, C<seek>, orthe C<< <FH> >> diamond operator will accept either a named filehandle
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -