随机读写

如果一个文件句柄是指向一个实体文件的,那么就可以对它进行随机数据的访问(包括随机读、写),随机访问表示可以读取文件中的任何一部分数据或者向文件中的任何一个位置处写入数据。实现这种随机读写的功能依赖于一个文件读写位置指针(file pointer)

当一个文件句柄关联到了一个实体文件后,就可以操作这个文件句柄,比如通过这个文件句柄去移动文件的读写指针,当这个指针指向第100个字节位置处时,就表示从100个字节处开始读数据,或者从100个字节处开始写数据。

可以通过seek()函数来设置读写指针的位置,通过tell()函数来获取文件读写指针的位置。如果愿意的话,IO::Seekable模块同样提供了一些等价的面向对象的操作方法,不过它们只是对seek、tell的一些封装,用法和工作机制是完全一样的。

需要注意的是,虽说文件读写指针不是属于文件句柄的,但通过文件句柄去操作读写指针的时候,可以认为指针是属于句柄的。例如,同一个文件的多个文件句柄的读写指针是独立的,如果文件A上同时打开了A1和A2两个文件句柄,那么在A1上设置的读写指针不会影响A2句柄的读写指针。

seek跳转文件指针位置

通过seek()函数,可以让文件的指针随意转到哪个位置。注意还有一个sysseek()函数,它们不同,seek()是工作在buffer上的,sysseek()是底层无buffer的。

seek FILEHANDLE, POSITION, WHENCE

seek()有三个参数:

  • 第一个参数是文件句柄
  • 第二个参数是正负整数或0,它的意义由第三个参数决定
  • 第三个参数是flag,用来表明相对与哪个位置进行跳转的,值可以是0、1和2。如果导入了Fcntl模块的seek标签(即use Fcntl qw(:seek)),则可以使用0、1、2对应的常量SEEK_SET、SEEK_CUR、SEEK_END来替代0、1、2

第三个参数的值决定第二个参数的意义。如下:

seek                           意义
-----------------------------------------------------------------
seek FH, $pos, 0 以相对于文件开头的位置跳转$pos个字节,
seek FH, $pos, SEEK_SET 即直接按照绝对位置的方式设置指针位置。
pos不能是负数,否则表示跳转到文件头的
前面,这会使得seek失败而返回0。例如
`seek FH, 0, 0`表示跳转到开头(第一个
字节前),`seek FH, 10, 0`表示跳转到第
10字节前 seek FH, $pos, 1 以相对于当前指针的位置向前(pos为正数)、
seek FH, $pos, SEEK_CUR 向后(pos为负数)跳转pos个字节,pos=0表
示保持原地不动。例如`seek FH, 60, 1`
表示指针向右(向前)移动60个字节。如果移
动到超出文件的位置并从这里写入数据,将
以空字节`\0`填充直到那个位置 seek FH, $pos, 2 以相对于文件尾部的位置跳转$pos个字节。
seek FH, $pos, SEEK_END 如果pos为负数,表示向文件头方向移动pos
个字节,如果pos为0则表示保持在尾部不动,
如果pos大于0且要写入,则会以空字节`\0`
填充直到那个位置。例如`seek FH, -60, 2`
表示从文件尾部向文件头部移动60个字节

seek在成功跳转成功时返回true,否则返回0值,例如想要跳转到文件头的前面,这时返回0且将指针放置到文件的结尾。

比如用seek来创建一个大文件:

open BIGFILE, ">", "bigfile.txt";
seek BIGFILE, 100*1024, 0; # 100K
syswrite BIGFILE, 'endendend' # 100k + 9bytes
close BIGFILE

跳转超出文件尾部后,如果要真的让文件扩充,需要在结尾的地方写入一点数据,否则不会填充。这就相当于用类似于下面的dd命令创建一个稀疏大文件一样。

dd if=/dev/zero of=bigfile seek=100 count=1 bs=1K

tell()函数获取文件指针位置

tell FILEHANDLE

tell函数获取给定文件句柄当前文件指针的位置。

唯一需要注意的一点是,如果文件句柄指向的文件描述符不是一个实体文件,比如套接字句柄,tell将返回-1。注意不是返回undef,尽管我们可能更期待它返回undef来判断。

$pos = tell MYHANDLE;
print "POS is", $pos > -1 ? $pos : "not file", "\n";

IO::Seekable

IO::Seekable模块提供了seek和tell的封装方法。例如:

$fh->seek($pos, 0);         # SEEK_SET
$fh->seek($pos, SEEK_CUR);
$pos = $fh->tell();

seek在EOF处读

就像实现tail -f一样监控每秒写入到文件尾部的数据并输出。如果使用seek来实现这个功能的话,参考如下:

#!/usr/bin/perl
use strict;
use warnings; die "give me a file" unless(@ARGV and -f $ARGV[0])
open my $taillog, $ARGV[0]; while(1){
while(<$tailog>){print "$.: $_";}
seek $taillog, 0, 1;
sleep 1;
}

上面的程序中,先读取出文件中的数据,然后将文件的指针保持在原地以便下次循环继续从这里开始读取,睡一秒后继续,这个逻辑并不难。

当然,对于上面简单的tail -f来说,根本没使用seek的必要,但是这提供了一种连续从尾部读取数据的思路。

seek在EOF处写

典型的是写日志文件,要不断地向文件尾部追加一行行日志数据。但是,多个进程可能会互相覆盖数据,因为不同进程的写真正是互相独立的,谁也不知道谁的指针在哪里。如果使用的是追加式写入方式,则多进程间不会出现数据覆盖的问题,因为每次append数据之前都会将指针放到文件的最结尾处。但是多个进程的append无法保证每行数据写入的顺序。

如果要保证某进程某次两行数据的写入是紧连在一起的,那么需要使用锁的方式,例如使用flock文件锁。

下面是一个简单的日志写入程序示例:

#!/usr/bin/perl
use strict;
use warnings;
use Fcntl qw(:flock :seek); sub logtofile {
die "give me two args" if @_ < 1;
my $logfile = shift;
my @msg = @_; open LOGFILE, ">>", $logfile or die "open failed: $!"; flock LOGFILE, LOCK_EX;
seek LOGFILE, 0, SEEK_END;
print LOGFILE @msg;
close LOGFILE;
} logtofile "/tmp/mylog.log", "msgA\n", "msgB\n", "msgC\n";

truncate截断文件

如果要截断文件为某个空间大小,直接使用truncate()函数即可(shell下也有truncate命令来截断文件)。

它的第一个参数是文件句柄,第二个参数是截断后的文件大小,单位字节。注意,truncate是从当前指针位置开始向后截断的,其指针前面(左边)的数据不会动但是会计算到截断后的大小。如果指定的截断大小超过文件大小,则会使用空字节\0填充到给定大小(这个行为默认没有定义)。

因为要截断,这个文件句柄的模式必须是可写的,且如果是使用">"模式,将首先被截断为空文件。所以,应该使用+<>>+>>这类模式。为了保证截断效果,如果使用的是后两种open模式,应该在每次截断前使用"seek"将指针指到文件的头部。

例如,截断文件为100字节大小。

open FILE, ">>", "bigfile";
seek FILE, 0, 0;
truncate FILE, 100;
close FILE;

按行截断文件

truncate只能按字节截断文件,不过有时候我们想按照行数来截断文件。

例如,想要保留前10行数据。实现的逻辑很简单,先按行读取10行(判断行号或使用一个行号计数器),然后记录下当前的指针位置,最后使用truncate截断到这个位置。

#!/usr/bin/perl

use strict;
use warnings; die "give me a file" unless @ARGV;
die "give me a line num" unless (defined($ARGV[1]) and $ARGV[1] >= 0); my $file = $ARGV[0];
my $trunc_to = int($ARGV[1]); # 读取到前X行
open READ, $file or die "open failed: $!";
while(<READ>){
last if $. == $trunc_to;
} my $trunc_size = tell READ;
exit if $. < $trunc_to; # total line less than $trunc_to
close READ; # truncate
open WRITE, "+<", $file or die "open failed: $!";
truncate WRITE, $trunc_size or die "truncate failed: $!";
close WRITE;

Perl IO:随机读写文件的更多相关文章

  1. Java基础之读文件——使用通道随机读写文件(RandomReadWrite)

    控制台程序,使用通道随机读写primes_backup.bin文件. import static java.nio.file.StandardOpenOption.*; import java.nio ...

  2. 计算机程序的思维逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读和写 不能随机读写,只能从头读到尾,且不能重复读,虽然通过缓冲可以实现部分重读,但 ...

  3. Commons IO方便读写文件的工具类

    Commons IO是apache的一个开源的工具包,封装了IO操作的相关类,使用Commons IO可以很方便的读写文件,url源代码等. 普通地读取一个网页的源代码的代码可能如下 InputStr ...

  4. Java IO如何读写文件

    Java把这些不同来源和目标的数据都统一抽象为数据流:Java语言的输入输出功能是十分强大而灵活的:在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上 ...

  5. Java编程的逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  6. 第五篇:使用无缓冲IO函数读写文件

    前言 本文介绍使用无缓冲IO函数进行文件读写. 所谓的无缓冲是指该IO函数通过调用系统调用实现,其实系统调用内部的读写实现也是使用了缓冲技术的. 读写步骤 1. 打开文件 open 函数 2. 读写文 ...

  7. 使用无缓冲IO函数读写文件

    前言 本文介绍使用无缓冲IO函数进行文件读写. 所谓的无缓冲是指该IO函数通过调用系统调用实现,其实系统调用内部的读写实现也是使用了缓冲技术的. 读写步骤 1. 打开文件 open 函数 2. 读写文 ...

  8. IO流 读写文件

    读写文件 如前所述,一个流被定义为一个数据序列.输入流用于从源读取数据,输出流用于向目标写数据. 下图是一个描述输入流和输出流的类层次图. 下面将要讨论的两个重要的流是 FileInputStream ...

  9. Python IO编程-读写文件

    1.1给出规格化得地址字符串,这些字符串是经过转义的能直接在代码里使用的字符串 需要导入os模块 import os >>>os.path.join('user','bin','sp ...

随机推荐

  1. 《SpringMVC从入门到放肆》九、SpringMVC注解式开发(简单参数接收)

    上一篇我们学习了注解式开发的配置方式并写了一个小Demo跑起来.今天我们来学习注解开发的参数接收.处理器方法中的常用参数有五类,这些参数会在系统调用时由系统自动赋值,即程序员可以在方法中直接使用.具体 ...

  2. ssm框架各自的作用

  3. 4.再来看看逆向——OD的简介

    目录 1.前言 2.一些设置和配置 3.开始了解OD 代码窗口 数据窗口 小端序问题 前言 前3节主要写了恶意代码用到的手段,接下来先写一下关于逆向调试的一些内容.毕竟逆向比较难理解一点. 一些配置和 ...

  4. javascript 零碎笔记

    使用 live-serve 这个工具,可以热更新 js 代码 逻辑运算符: 常用于单边条件判断,比如 真判断(获取子属性) {error && <div className=&q ...

  5. python面向对象的知识梳理

    面向对象(Object Oriented Programming) 三个基本特征: 1.封装:包含两个概念,对象将变量(状态)和方法(用来改变状态或执行涉及状态的计算)集中在一个地方—即对象本身. 通 ...

  6. NPOI 修改指定单元格字体颜色

    //创建一个字体颜色 IFont font = hssfworkbook.CreateFont(); //红色 font.Color = HSSFColor.Red.Index; //样式 ICell ...

  7. unittest中的Empty suite错误

    import unittest from selenium import webdriver class ibdata(unittest.TestCase): @classmethod def set ...

  8. linux操作命令之压缩命令

    常用的压缩格式:    .zip     .gz     .bz2 一..zip格式压缩 zip 压缩文件名 源文件 压缩文件 zip -r 压缩文件名 源目录 压缩目录 解压缩 unzip 压缩文件 ...

  9. Hadoop 集群安装(从节点安装配置)

    1.Java环境配置 view plain copy sudo mv /tmp/java /opt/ jdk安装完配置环境变量,编辑/etc/profile: view plain copy sudo ...

  10. 超有料丨小白如何成功逆袭为年薪30万的Web安全工程师

    今天的文章是一篇超实用的学习指南,尤其是对于即将毕业的学生,新入职场的菜鸟,对Web安全感兴趣的小白,真的非常nice,希望大家能够好好阅读,真的可以让你少走很多弯路,至少年薪30万so easy! ...