UNIX环境高级编程——文件操作之(read,write,lseek)


UNIX文件操作是最常见的问题,其中一些细节问题将在本文提及到,以下是博文将讨论到的问题

这里有几个前提:使用环境是DISK上的文件:其他情况会有以下几个不同

  • socket:read,write在socket下使用会出现延迟而返回值与期望读写的字节数不一样
  • 终端文件的读写实际返回大小是行字节数的大小


1.read()函数用法

	int fd;
	int get_count;
	char * contact; 
	contact = (char*)malloc(6);
	memset(contact,0,6);	

	
	fd=open("file",O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		ssize_t i;
		off_t lseek_count;
		while(1)
		{
			lseek_count = lseek( fd,0, SEEK_CUR);//计算当前文件指针的位置
			i=read(fd,contact,5);
			if( i <= 0)
			{	
				cout<<"读取file出错!"<<lseek_count<<" 内容="<<contact<<" 个数="<<i<<endl;
				break;

			}else
			{	
				cout<<"#1.正常读:文件指针值="<<lseek_count<<" 内容="<<contact<<" 个数="<<i<<endl;
			}
			memset(contact,0,6);
		}	
		close(fd);
	}
文件内容:

执行结果:

read()总结:

  • read()循环读取一个文件全部内容时,特别注意最后一次循环,往往最后一次读取的字符数比count少,所以不能用read() = =count来检查错误
  • read读到EOF时返回的是0,而不是-1.例如上图最后一行。(-1是读取字符时错误)

2.write()函数用法

(1)正常写原本有内容的文件(覆盖写,而且只覆盖写入的大小)

	/* O_RDWR 方式写*/
	char str[] ="abcde";
	fd=open("file", O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		off_t lseek_count = lseek( fd,0, SEEK_CUR);//获取文件指针的位置
		int count = write(fd,str,5);
		if( count <= 0)
		{	
			cout<<"写取file出错!"<<endl;
		}else
		{	
			cout<<"#1. O_RDWR方式写文件:指针值="<<lseek_count<<" 写入内容="<<str<<" 写入字符个数="<<str<<endl;
		}	
		close(fd);
	}
	
执行前:


执行后:(前五位被覆盖)

(2)用lseek移动位置来写(不从第0位开始写)

	/* O_RDWR 方式打开并且LSEEK移动文件指针写*/
	char str[] ="abcde";
	fd=open("file", O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		off_t lseek_count = lseek( fd,9, SEEK_SET);//移动文件指针的位置
		int count = write(fd,str,5);
		if( count <= 0)
		{	
			cout<<"写取file出错!"<<endl;
		}else
		{	
			cout<<"#1. O_RDWR方式写文件:指针值="<<lseek_count<<" 写入内容="<<str<<" 写入字符个数="<<str<<endl;
		}	
		close(fd);
	}
执行前后:(前面没覆盖的保留)


(3)空洞

	/*空洞写*/
	char str[] ="abcde";
	fd=open("file", O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		off_t lseek_count = lseek( fd,9, SEEK_END);//在文件结尾的后9位开始写(中间将会出现9个字节的空洞)
		int count = write(fd,str,5);
		if( count <= 0)
		{	
			cout<<"写取file出错!"<<endl;
		}else
		{	
			cout<<"#1. O_RDWR方式写文件:指针值="<<lseek_count<<" 写入内容="<<str<<" 写入字符个数="<<str<<endl;
		}	
		close(fd);
	}
	/*带有空洞文件的读*/
	fd=open("file",O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		ssize_t i;
		off_t lseek_count;
		while(1)
		{
			lseek_count = lseek( fd,0, SEEK_CUR);//计算当前文件指针的位置
			i=read(fd,contact,5);
			if( i <= 0)
			{	
				cout<<"读取file出错!"<<lseek_count<<" 内容="<<contact<<" 个数="<<i<<endl;
				break;

			}else
			{	
				cout<<"#1.正常读:文件指针值="<<lseek_count<<" 内容="<<contact<<" 个数="<<i<<endl;
			}
			memset(contact,0,6);
		}	
		close(fd);
	}
执行结果:

<1>中间一连串‘\0’就是空洞,空洞可以正常read()。

<2>在程序中,带空洞的文件read()处理需要小心字符串问题,如读出出来为"\0\0\0ab",字符串处理时会认为是空串,因为开头就是NULL。

(4)写入块大小问题

ssize_t write(int fd, const void *buf, size_t count);
RETURN VALUE
       On success, the number of bytes written is returned (zero indicates nothing was written).  It is not an error if this number is  smaller  than  the
       number of bytes requested; this may happen for example because the disk device was filled.  See also NOTES.

       On error, -1 is returned, and errno is set appropriately.

       If  count  is  zero and fd refers to a regular file, then write() may return a failure status if one of the errors below is detected.  If no errors
       are detected, or error detection is not performed, 0 will be returned without causing any other effect.  If count is zero and fd refers to  a  file
       other than a regular file, the results are not specified.

以上是手册说明:write()会把buf前count个字节写入文件,但现实当中往往会出现的是溢出问题,考虑到一下的情况:

	/* 一种由于count控制错误而出错的情况*/
	char str[] ="ab";//str是一个长度为3的字符数组
	fd=open("file", O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		off_t lseek_count = lseek( fd,0, SEEK_CUR);//计算当前指针位置文件指针的位置
		int count = write(fd,str,5);//注意的里要求写入的count为5,大于str的长度。
		if( count <= 0)
		{	
			cout<<"写取file出错!"<<endl;
		}else
		{	
			cout<<"#1. O_RDWR方式写文件:指针值="<<lseek_count<<" 写入内容="<<str<<" 写入字符个数="<<count<<endl;
		}	
		close(fd);
	}
执行前的文件内容:


执行后:

结果并不应为str大小为三个字符而只写入三个字符,而是写入由count=5来决定写入个数,所以程序会继续越界直到写入个数为5个字符才停止。

也许你会难以理解为什么会是2个‘\0’,但通过GDB改写内存后得出的结果你就会明白这是越界后的不确定值:

结果(上面改写了str数组后面内存的值,原来是‘\000’ '\034'改成‘z’'\034'):


所以程序必须根据条件控制count的大小。

write总结:

  • 无APPEND,TURNC时,write()只覆盖写入的区域,其他不变。但实际情况是,要么用O_APPEND追加写或者O_TURNC截断重写。
  • write()要根据实际情况改变count,不然会出现多写问题。

3.lseek()用法

文件指针,是指向将要操作的文件内容的字节位置,刚打开文件时,无论O_APPEND,O_TURNC方式,都是以下形式(假设文件内容为:abc,注意EOF不是显示存在文件里的)

SEEK_CUR:移动的字节数是相对当前文件指针位置;

SEEK_SET:移动的字节数是绝对位置;

SEEK_END:移动的字节数是相对当前文件末尾(EOF)位置;

把文件看做数组,文件的第一个字符位置标号为0,最后一位是文件结尾符。每操作一次(read,write)时,文件指针会移动相应的字节数。

(1)统计文件字节数

off_t lseek_count = lseek( fd, 0, SEEK_END);//获取文件的长度方法,继续其他读写操作时记得复位

(2)计算当前文件指针位置

off_t lseek_count = lseek( fd,0, SEEK_CUR);


4.APPEND打开问题

(1)read()使用APPEND时无效,还是从0位置开始读,且可以通过lseek移动文件指针位置。

(2)write()使用APPEND时,lseek()改变不了写入的位置(注意:意思是就算用lseek改变位置时,但到write()时会自动改成文件末尾位置进行写),只能在文件末尾追加。


5.O_TRUNC打开问题

(1)使用fd=open("file",O_TRUNC|O_RDWR);打开文件时,将什么也读不到,因为先把文件截空,再读。

(2)一般O_TRUNC写问题

	/* O_TRUNC|O_RDWR 方式写*/
	char str[] ="abcde";
	fd=open("file", O_TRUNC|O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		off_t lseek_count = lseek( fd,0, SEEK_CUR);//获取文件指针的位置
		if(write(fd,str,5) != 5)
		{	
			cout<<"写取file出错!"<<endl;
		}else
		{	
			cout<<"#1 O_TRUNC|O_RDWR方式写文件指针值="<<lseek_count<<" 内容="<<str<<endl;
		}	
		close(fd);
	}

执行前:


执行后:观察与2.write()的不同


(3)O_TRUNC且不从起始位0开始写问题

	/* O_TRUNC|O_RDWR 并且移动lseek方式写*/
	char str[] ="abcde";
	fd=open("file", O_TRUNC|O_RDWR);
	if(fd<0)
	{
		cout<<"打开file出错!"<<endl;	
	}else
	{
		off_t lseek_count = lseek( fd,3, SEEK_SET);//lseek移动到位置3
		if(write(fd,str,5) != 5)
		{	
			cout<<"写取file出错!"<<endl;
		}else
		{	
			cout<<"#1 O_TRUNC|O_RDWR方式写文件指针值="<<lseek_count<<" 内容="<<str<<endl;
		}	
		close(fd);
	}


执行前:


执行后:(观察与2.write()的不同)


O_TURNC总结:

  • O_TURNC相当于把文件截断为空文件,除非要重写文件,其他情况比较少使用。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值