《UNIX环境高级编程》笔记--read函数,write函数,lseek函数

2024-02-21 09:38

本文主要是介绍《UNIX环境高级编程》笔记--read函数,write函数,lseek函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.read函数

调用read函数从文件去读数据,函数定义如下:
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <unistd.h>   
  2. ssize_t read(int filedes, void* buff, size_t nbytes);  
#include <unistd.h>
ssize_t read(int filedes, void* buff, size_t nbytes);
成功则返回实际读取的byte数,如果已经达到文件结尾则返回0,出错则返回-1.

2.write函数

调用write函数向打开的文件写入数据,函数定义如下:
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <unistd.h>   
  2. ssize_t write(int filedes, void* buff, size_t nbytes);  
#include <unistd.h>
ssize_t write(int filedes, void* buff, size_t nbytes);
成功则返回实际写入的byte数,出错则返回-1.

read和write的buff大小为一个文件block大小时,效率是最高的,在ext4文件系统中,一个文件block大小为4K,一般这个块长存放
在stat结构中,定义如下:
blksize_t st_blksize;
可以使用stat系列函数获取该值。

注意:
使用read,write操作管道,FIFO以及某些设备时,特别是终端,网络和STREAMS,有下列两种性质。
a.一次read操作所返回的数据可能少于所要求的数据,即使还没达到文件尾端也可能是这样。这不是一个错误,应该
继续读该设备。
b.一次write操作的返回值也可能少于指定输出的字节数。这也不是错误,应当继续写余下的数据至该设备。

3.lseek函数

每个打开的文件都有一个关联的“当前偏移量”,用于记录从文件到当前当前位置的偏移字节数,lseek函数是设置这个当前偏移量

的函数,函数的声明如下:
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <unistd.h>   
  2. off_t lseek(int filedes, off_t offset, int whence);  
#include <unistd.h>
off_t lseek(int filedes, off_t offset, int whence);
成功则返回新的文件偏移量,失败则返回-1.

如果whence是SEEK_SET,则文件的偏移量设置为文件开始加上offset个字节。
如果whence是SEEK_CUR,则文件的偏移量设置为当前偏移量开始加上offset个字节,offset可正可负。
如果whence是SEEK_END,则文件的偏移量设置为文件长度加上offset个字节,offset可正可负。

不是每个文件都能够设置偏移量,有些文件如管道,FIFO或socket,无法设置偏移量,可以使用如下函数测试是否可以设置偏移量,
如果返回-1,则表示不可以。
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. off_t currpos;  
  2. currpos = lseek(fd, 0, SEEK_CUR);  
off_t currpos;
currpos = lseek(fd, 0, SEEK_CUR);

创建一个文件,往文件中写入10个字符,然后再使用lseek定位文件开始加上4个字节的偏移量,然后读取接下来的内容。
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <fcntl.h>   
  2. #include <stdio.h>   
  3.   
  4. int main(void){  
  5.         int fd,byteNum,result;  
  6.         char wbuf[10] = "123456789";  
  7.         char rbuf[10];  
  8.         if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){  
  9.                 perror("open");  
  10.                 return -1;  
  11.         }  
  12.   
  13.         if((byteNum = write(fd, wbuf, 10))<0){  
  14.                 perror("write");  
  15.                 return -1;  
  16.         }  
  17.   
  18.         if((result = lseek(fd, 4, SEEK_SET))<0){  
  19.                 perror("lseek");  
  20.                 return -1;  
  21.         }  
  22.   
  23.         if((byteNum = read(fd, rbuf, 10)) < 0){  
  24.                 perror("read");  
  25.                 return -1;  
  26.         }  
  27.   
  28.         printf("read content:%s\n",rbuf);  
  29.   
  30.         close(fd);  
  31.         return 0;  
  32. }  
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, 4, SEEK_SET))<0){
perror("lseek");
return -1;
}
if((byteNum = read(fd, rbuf, 10)) < 0){
perror("read");
return -1;
}
printf("read content:%s\n",rbuf);
close(fd);
return 0;
}
运行结果:
read content:56789

如果将偏移量设置为文件开始加上一个负数,调用lseek就会出错。
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <fcntl.h>   
  2. #include <stdio.h>   
  3.   
  4. int main(void){  
  5.         int fd,byteNum,result;  
  6.         char wbuf[10] = "123456789";  
  7.         char rbuf[10];  
  8.         if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){  
  9.                 perror("open");  
  10.                 return -1;  
  11.         }  
  12.   
  13.         if((byteNum = write(fd, wbuf, 10))<0){  
  14.                 perror("write");  
  15.                 return -1;  
  16.         }  
  17.   
  18.         if((result = lseek(fd, -1, SEEK_SET))<0){  
  19.                 perror("lseek");  
  20.                 return -1;  
  21.         }  
  22.   
  23.         if((byteNum = read(fd, rbuf, 10)) < 0){  
  24.                 perror("read");  
  25.                 return -1;  
  26.         }  
  27.   
  28.         printf("read content:%s\n",rbuf);  
  29.   
  30.         close(fd);  
  31.         return 0;  
  32. }  
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, -1, SEEK_SET))<0){
perror("lseek");
return -1;
}
if((byteNum = read(fd, rbuf, 10)) < 0){
perror("read");
return -1;
}
printf("read content:%s\n",rbuf);
close(fd);
return 0;
}
运行结果:
lseek: Invalid argument

是不是offset就一定不能为负数呢,不一定,只要最终的偏移量不小于0就可以了,看下面的例子。
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <fcntl.h>   
  2. #include <stdio.h>   
  3.   
  4. int main(void){  
  5.         int fd,byteNum,result;  
  6.         char wbuf[10] = "123456789";  
  7.         char rbuf[10];  
  8.         if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){  
  9.                 perror("open");  
  10.                 return -1;  
  11.         }  
  12.   
  13.         if((byteNum = write(fd, wbuf, 10))<0){  
  14.                 perror("write");  
  15.                 return -1;  
  16.         }  
  17.   
  18.         if((result = lseek(fd, -4, SEEK_CUR))<0){  
  19.                 perror("lseek");  
  20.                 return -1;  
  21.         }  
  22.   
  23.         if((byteNum = read(fd, rbuf, 10)) < 0){  
  24.                 perror("read");  
  25.                 return -1;  
  26.         }  
  27.   
  28.         printf("read content:%s\n",rbuf);  
  29.   
  30.         close(fd);  
  31.         return 0;  
  32. }  
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR|S_IXUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, -4, SEEK_CUR))<0){
perror("lseek");
return -1;
}
if((byteNum = read(fd, rbuf, 10)) < 0){
perror("read");
return -1;
}
printf("read content:%s\n",rbuf);
close(fd);
return 0;
}
运行结果:
read content:789

如果文件偏移量大于文件长度再写入数据,那么生成的文件就会出现空洞。先往文件写入10个字符,再跳过40960个字符,再写入10个字符。
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <fcntl.h>   
  2. #include <stdio.h>   
  3.   
  4. int main(void){  
  5.         int fd,byteNum,result;  
  6.         char wbuf[10] = "123456789";  
  7.         char rbuf[10];  
  8.         if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR))<0){  
  9.                 perror("open");  
  10.                 return -1;  
  11.         }  
  12.   
  13.         if((byteNum = write(fd, wbuf, 10))<0){  
  14.                 perror("write");  
  15.                 return -1;  
  16.         }  
  17.   
  18.         if((result = lseek(fd, 40960, SEEK_END))<0){  
  19.                 perror("lseek");  
  20.                 return -1;  
  21.         }  
  22.   
  23.         if((byteNum = write(fd, wbuf, 10)) < 0){  
  24.                 perror("write");  
  25.                 return -1;  
  26.         }  
  27.   
  28.         close(fd);  
  29.         return 0;  
  30. }  
#include <fcntl.h>
#include <stdio.h>
int main(void){
int fd,byteNum,result;
char wbuf[10] = "123456789";
char rbuf[10];
if((fd = open("./a.txt", O_RDWR|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR))<0){
perror("open");
return -1;
}
if((byteNum = write(fd, wbuf, 10))<0){
perror("write");
return -1;
}
if((result = lseek(fd, 40960, SEEK_END))<0){
perror("lseek");
return -1;
}
if((byteNum = write(fd, wbuf, 10)) < 0){
perror("write");
return -1;
}
close(fd);
return 0;
}
运行结果:
-rw------- 1 root root 40980 2013-09-09 15:03 a.txt
使用od命令查看文件内容:
root@virtual-machine:~# od -c a.txt
0000000   1   2   3   4   5   6   7   8   9  \0  \0  \0  \0  \0  \0  \0
0000020  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0
*
0120000  \0  \0  \0  \0  \0  \0  \0  \0  \0  \0   1   2   3   4   5   6
0120020   7   8   9  \0
0120024
空洞的内容全部置为0

查看下文件占用的磁盘大小:
8 -rw------- 1 root root 40980 2013-09-09 15:41 a.txt
文件虽然有40980个字节,只占用了8个block,即8K(这边的block不是文件系统的block,而是kernel block,linux中,
kernel block大小为1K),如果是没有空洞的文件,则占用磁盘大小应该是44K。可见,虽然有空洞,但是实际存储时没
有占用文件大小的磁盘空间。

如果文件以O_APPEND方式打开,但是在写入前lseek到文件的某一个位置,结果会怎样?
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>   
  2. #include <fcntl.h>   
  3.   
  4. int main(void){  
  5.         int fd,result;  
  6.         char wbuf[5] = "1234";  
  7.         if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){  
  8.                 perror("open");  
  9.                 return -1;  
  10.         }  
  11.   
  12.         if((result = lseek(fd, 2, SEEK_SET)) < 0){  
  13.                 perror("lseek");  
  14.                 return -1;  
  15.         }  
  16.   
  17.         if((result = write(fd, wbuf, 4))<0){  
  18.                 perror("write");  
  19.                 return -1;  
  20.         }  
  21.   
  22.         close(fd);  
  23.         return 0;  
  24. }  
#include <stdio.h>
#include <fcntl.h>
int main(void){
int fd,result;
char wbuf[5] = "1234";
if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){
perror("open");
return -1;
}
if((result = lseek(fd, 2, SEEK_SET)) < 0){
perror("lseek");
return -1;
}
if((result = write(fd, wbuf, 4))<0){
perror("write");
return -1;
}
close(fd);
return 0;
}
程序执行前a.txt为:123456789
程序执行后a.txt为:
123456789
1234
因为设置为O_APPEND后,内核每次对这种文件写之前,进程当前的偏移量都会设置到文件的末尾。而且lseek和write
合起来是一个原子操作。

在使用lseek函数的时候发现一个问题,如果调用如下:lseek(fd, -2, SEEK_END),则还是在文件的末尾写入,不知道
为什么。
[cpp] view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. #include <stdio.h>   
  2. #include <fcntl.h>   
  3.   
  4. int main(void){  
  5.         int fd,result;  
  6.         char wbuf[5] = "1234";  
  7.         if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){  
  8.                 perror("open");  
  9.                 return -1;  
  10.         }  
  11.   
  12.         if((result = lseek(fd, -2, SEEK_END)) < 0){  
  13.                 perror("lseek");  
  14.                 return -1;  
  15.         }  
  16.   
  17.         if((result = write(fd, wbuf, 4))<0){  
  18.                 perror("write");  
  19.                 return -1;  
  20.         }  
  21.   
  22.         close(fd);  
  23.         return 0;  
  24. }  
#include <stdio.h>
#include <fcntl.h>
int main(void){
int fd,result;
char wbuf[5] = "1234";
if((fd=open("a.txt",O_WRONLY|O_APPEND))<0){
perror("open");
return -1;
}
if((result = lseek(fd, -2, SEEK_END)) < 0){
perror("lseek");
return -1;
}
if((result = write(fd, wbuf, 4))<0){
perror("write");
return -1;
}
close(fd);
return 0;
}
运行结果:
yan@yan-vm:~/ctest$ od -c a.txt
0000000   1   2   3  \n
0000004
yan@yan-vm:~/ctest$ ./a.out
yan@yan-vm:~/ctest$ od -c a.txt
0000000   1   2   3  \n   1   2   3   4
0000010

这篇关于《UNIX环境高级编程》笔记--read函数,write函数,lseek函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/731344

相关文章

通过Docker容器部署Python环境的全流程

《通过Docker容器部署Python环境的全流程》在现代化开发流程中,Docker因其轻量化、环境隔离和跨平台一致性的特性,已成为部署Python应用的标准工具,本文将详细演示如何通过Docker容... 目录引言一、docker与python的协同优势二、核心步骤详解三、进阶配置技巧四、生产环境最佳实践

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

从基础到高级详解Python数值格式化输出的完全指南

《从基础到高级详解Python数值格式化输出的完全指南》在数据分析、金融计算和科学报告领域,数值格式化是提升可读性和专业性的关键技术,本文将深入解析Python中数值格式化输出的相关方法,感兴趣的小伙... 目录引言:数值格式化的核心价值一、基础格式化方法1.1 三种核心格式化方式对比1.2 基础格式化示例

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C