Perl IO:操作系统层次的IO
sysopen()open()和sysopen()都打开文件句柄,open()是比较高层次的打开文件句柄,sysopen()相对要底层一点。但它们打开的文件句柄并没有区别,只不过sysopen()有一些自己的特性:可以使用几个open()没有的flag,可以指定文件被创建时的权限等。 一定要注意的是,io buffer和open()、sysopen()无关,而是和读、写的方式有关,例如read()、getc()以及行读取都使用io buffer,而sysread、syswrite则直接绕过io buffer。 例如: sysopen HANDLE,"file.txt",O_RDONLY; open HANDLE,$filename,O_WRONLY|O_CREAT,0644; open HANDLE,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH; 其中sysopen支持的flag部分有以下几种: # 三种基本读写模式 O_RDONLY O_RDWR O_WRONLY # 配合基本读写模式的额外模式 O_APPEND O_TRUNC O_CREAT O_EXCL 只能配合CREAT使用,只有文件 不存在时才创建,文件存在时直 接失败而不是打开它 O_BINARY 二进制模式,表示不做换行符转换 O_TEXT 文本模式,表示做换行符转换 O_NONBLOCK 非阻塞模式 O_NDELAY 同上,另一种表示方式,为了可移 植性,建议使用上面的模式 非阻塞读写sysopen()比open()多出的一个好用的特性就是 use Fcntl; open HANDLE,'/dev/ttyS0',O_RDONLY | O_NONBLOCK; # sysread一个字符,但不会阻塞 my $key; while(sysread HANDLE,$key,1){ if (defined $key){ print "got $keyn"; } else { do other tasks; } sleep 1; } close HANDLE;
use Fcntl; open HANDLE,1){ if (defined $key){ print "got $keyn"; } else { if($! == 'EAGAIN'){ do other tasks; sleep 1; } else { warn "Error read: $!"; last; } } } close HANDLE; IO::File自动探测(sys)open使用
例如: # open() my $fh = IO::File->new($filename,"+<"); # sysopen() my $fh = IO::File->new($filename,O_RDWR); my $fh = IO::File->new($file,O_RDWR,0644); sysread()sysread()实现操作系统层的读操作,它通过read()系统调用直接读取文件描述符,会直接绕过IO Buffer层。 sysread FILEHANDLE,SCALAR,LENGTH,OFFSET sysread FILEHANDLE,LENGTH 表示从FILEHANDLE文件句柄中读取LENGTH字节长度的数据保存到标量SCALAR中,如果指定了OFFSET,则从标量的OFFSET位置处开始写入读取到的数据。sysread()的返回值是读取到的字节数。 下面是一个结合O_NONBLOCK修饰符的sysread()。 #!/usr/bin/perl use strict; use warnings; use Fcntl; sysopen my $fh,$ARGV[0],O_RDONLY | O_NONBLOCK or die "open failed: $!"; my $data; my $size = sysread $fh,$data,20; if ($size == 20 ){ # 已读取20字节 # 继续读取30字节追加到$data尾部 $size += sysread $fh,30,20; print "已读数据为: $datan"; if ($size < 50){ print "文件大小为$size,数据不足50字节n"; }else{ print "已读字节数:$sizen"; } } elsif($size > 0) { print "文件大小为$size,数据不足20字节n"; } else { print "空文件n"; } 在上面的代码中,主要是sysread()和O_NONBLOCK需要解释下。如果没有O_NONBLOCK,那么sysread()在读取不到20字节、50字节时将被阻塞等待。但现在的情况下,如果数据不足20、50字节时,sysread()将直接返回,并返回读取到的字节数(小于20或30)。 这里如果sysread()替换成read(),它们的区别是,sysread()只读取20或50字节数据,不会多读任何一个字节,而read()则可能多读一些数据到IO Buffer中,但是只取其中的20或50字节,剩余的数据遗留在IO Buffer中。 于是,文件IO的指针就出现了不同值,对于sysread,他的指针就是20或50位置处,但是read()读取数据时,底层的文件指针将可能出现在1000字节处,但IO buffer中的IO指针将在20或50处。根据这个差值,可以计算出IO Buffer中保存了多少字节的数据。见后文sysseek()。 最后注意,不存在sySEOf()这样的函数来判断是否读取到了文件的结尾。但是,可以通过读取数据的返回值(即读了多少字节)来判断是否到了文件结尾。 syswrite()syswrite()实现操作系统层的写操作,它通过write()系统调用直接向文件描述符中写入数据,它会直接绕过IO Buffer层。 syswrite FILEHANDLE,SCALAR syswrite FILEHANDLE,LENGTH syswrite FILEHANDLE,OFFSET 如果只有两个参数,则直接将标量SCALAR代表的数据写入到FILEHANDLE中,如果指定了LENGTH,则从SCALAR中取LENGTH字节长度的数据写入到FILEHANDLE中,如果指定了OFFSET,则表示从标量的OFFSET处开始截取LENGTH长度的数据写入到FILEHANDLE中。OFFSET可以指定为负数,这表示从尾部开始数写入OFFSET个字节的数据。如果LENGTH大于从OFFSET开始计算可以获取到的数据,则能获取到多少数据就写入多少数据。 syswrite()返回实际写入的字节数,如果出错了则返回undef。 例如: # 写入abcdef syswrite STDOUT,"abcdef"; # 写入abc syswrite STDOUT,"abcdef",3; # 写入cde syswrite STDOUT,3,2; # 写入cde syswrite STDOUT,-4; 实际上,不适用syswrite(),直接使用print也可以绕过io buffer,但前提是设置文件句柄的IO层为 binmod(FILEHANDLE,":unix"); 例如下面的例子中,在10秒内将每秒输出一个点,如果把binmode那行删除,将在10秒之后一次性输出10个点。如果删除binmode,还可以将 #!/usr/bin/perl binmode(STDOUT,":unix"); for (0..9){ print "."; # syswrite STDOUT,"."; sleep 1; } print "n"; sysseek()sysseek FILEHANDLE,POSITION,WHENCE sysseek()通过lseek()系统调用设置或返回IO指针的位置,它直接绕过IO Buffer操作文件描述符。它和seek()的语法上没什么区别: # seek using whence numbers sysseek HANDLE,0; # rewind to start sysseek HANDLE,2; # seek to end of file sysseek HANDLE,20,1; # seek forward 20 characters # seek using Fcntl symbols use Fcntl qw(:seek); sysseek HANDLE,SEEK_SET; # rewind to start sysseek HANDLE,SEEK_END; # seek to end of file sysseek HANDLE,SEEK_CUR; # seek forward 20 characters sysseek()返回设置IO指针位置后的新位置。例如原来IO指针位置为第三个字节处,向前移动5字节后,sysseek()将返回8。所以,可以通过sysseek()来实现tell()函数的功能,只需将sysseek()相对于当前位置移动0字节即可: sysseek(HANDLE,SEEK_CUR); 除了绕过IO buffer,sysseek()和seek()基本相同。但正是它绕过了io buffer,导致了sysseek()和tell()的结果可能大不一样,tell()获取的是IO Buffer中IO指针的位置,而 下面是一个说明tell()和sysseek()区别的示例: #!/usr/bin/perl use strict; use warnings; use 5.010; use Fcntl q(:seek); open my $fh,"<","abc.log"; my $readed; read $fh,$readed,5; print "tell pos: ",tell($fh); print "sysseek pos: ",sysseek($fh,SEEK_CUR); 向abc.log中写入10个字节(实际为11个字节,因为echo自动加换行符)的数据: $ echo "0123456789" >abc.log 执行上面的Perl程序: tell pos: 5 sysseek pos: 11 上面的程序中,使用read()函数读取了5个字节数据,但是read()是使用IO Buffer的,它会从文件描述符中多读取一些数据到IO Buffer中(一般是几K的数据),例如这里最多只能读11字节到io buffer,然后从io buffer中返回指定数量5字节的数据保存到 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |