Perl IO:随机读写文件
随机读写
如果一个文件句柄是指向一个实体文件的,那么就可以对它进行随机数据的访问(包括随机读、写),随机访问表示可以读取文件中的任何一部分数据或者向文件中的任何一个位置处写入数据。实现这种随机读写的功能依赖于一个文件读写位置指针(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:随机读写文件的更多相关文章
- Java基础之读文件——使用通道随机读写文件(RandomReadWrite)
控制台程序,使用通道随机读写primes_backup.bin文件. import static java.nio.file.StandardOpenOption.*; import java.nio ...
- 计算机程序的思维逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库
57节介绍了字节流, 58节介绍了字符流,它们都是以流的方式读写文件,流的方式有几个限制: 要么读,要么写,不能同时读和写 不能随机读写,只能从头读到尾,且不能重复读,虽然通过缓冲可以实现部分重读,但 ...
- Commons IO方便读写文件的工具类
Commons IO是apache的一个开源的工具包,封装了IO操作的相关类,使用Commons IO可以很方便的读写文件,url源代码等. 普通地读取一个网页的源代码的代码可能如下 InputStr ...
- Java IO如何读写文件
Java把这些不同来源和目标的数据都统一抽象为数据流:Java语言的输入输出功能是十分强大而灵活的:在Java类库中,IO部分的内容是很庞大的,因为它涉及的领域很广泛:标准输入输出,文件的操作,网络上 ...
- Java编程的逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库
本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...
- 第五篇:使用无缓冲IO函数读写文件
前言 本文介绍使用无缓冲IO函数进行文件读写. 所谓的无缓冲是指该IO函数通过调用系统调用实现,其实系统调用内部的读写实现也是使用了缓冲技术的. 读写步骤 1. 打开文件 open 函数 2. 读写文 ...
- 使用无缓冲IO函数读写文件
前言 本文介绍使用无缓冲IO函数进行文件读写. 所谓的无缓冲是指该IO函数通过调用系统调用实现,其实系统调用内部的读写实现也是使用了缓冲技术的. 读写步骤 1. 打开文件 open 函数 2. 读写文 ...
- IO流 读写文件
读写文件 如前所述,一个流被定义为一个数据序列.输入流用于从源读取数据,输出流用于向目标写数据. 下图是一个描述输入流和输出流的类层次图. 下面将要讨论的两个重要的流是 FileInputStream ...
- Python IO编程-读写文件
1.1给出规格化得地址字符串,这些字符串是经过转义的能直接在代码里使用的字符串 需要导入os模块 import os >>>os.path.join('user','bin','sp ...
随机推荐
- EXCLE 导入 或 导出
首先要引用 NPOI.dll (可在网上下载!)//导入public void OnSubmit() { string path = Server.MapPat ...
- html+css手机端自动适应
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scal ...
- BOM与DOM操作
BOM: OM-JavaScript是运行在浏览器中的,所以提供了一系列对象用于和浏览器窗口进行交互,这些对象主要包括window.document.location.navigator和screen ...
- unittest生产html测试报告
需要添加HTMLTestRunner.py文件,我用的ubuntu16.04下的python3.5.2,所以我放在/usr/lib/python3.5下 import unittest import ...
- External Snapshot management
External Snapshot management Symptom As of at least libvirt 1.1.1, external snapshot support is inco ...
- C# 控制台应用程序中输出彩色字体
using System; class Example { public static void Main() { // Get a string array with the names of Co ...
- 安卓开发笔记(十六):'Request(okhttp3.Request.Builder)' has private access in 'okhttp3.Request
当出现了'Request(okhttp3.Request.Builder)' has private access in 'okhttp3.Request的错误的时候,实际上是我们在写代码的时候少打了 ...
- Batch入门教程丨第一章:部署与Hello World!(下)
在上期分享的内容中,我们已经掌握了基础理论知识,今天我们将继续了解和学习与Windows Batch有关的知识和编程方法,如何编写和运行Windows Batch程序,脚本语言的入门方式等,从而能够更 ...
- Ubuntu12.04下安装NS3.25
实验室项目的需求,要使用到NS3做仿真,使用实验室的电脑,Ubuntu12.04,版本比较低,建议大家使用16.04,这样安装依赖文件时不会出现版本过低的问题 (没管最后也安装成功了).下面就安装步骤 ...
- Python中面向对象的概念(科普)
面向对象(OOP)基本概念 面向对象编程 —— Object Oriented Programming 简写 OOP 目标 了解 面向对象 基本概念 01. 面向对象基本概念 我们之前学习的编程方式就 ...