三、文件IO——系统调用(续)

时间:2022-07-18 14:52:19

3.2.4 read 函数--- 读文件

  read(由已打开的文件读取数据)

 #include<unistd.h>
ssize_t read(int fd, void * buf, size_t count);
  • * 函数说明
    • read() 会把参数 fd 所指的文件传送 count 个字节到 buf 指针所指的内存中。
    • 若参数 count 为0,则 read() 不会有作用并返回0.
    • 返回值为实际读取到的字节数,如果返回0,表示已到达文件尾或是无可读取的数据,此外文件读写位置会随读取到的字节移动
  • * 参数
    • fd 为先前由 open() 或 creat() 所返回的文件描述词。
    • buf  存放读取数据的缓存
    • count 要求读取一次数据的字节数
  • * 附加说明
    • 如果顺利 read() 会返回实际读到的字节数,最好能将返回值与参数 count 做比较,若返回的字节数要比要求读到的字节数少,则有可能读到了文件尾、从管道(pipe)或终端机读取,或者是 read() 被信号中断了读取动作。当有错误发生时,则返回 -1,错误代码存入 errno 中,而文件读写位置则无法预期,
  • * 错误代码
    • EINTR 此调用被信号所中断
    • * EAGAIN 当使用不可阻断 I/O 时(O_NONBLOCK),若无数据可读取则返回此值
    • * EBADF 参数 fd 非有效的文件描述词,或该文件已关闭
  • 由多种情况可使实际督导的字节数少于要求读写字节数
    • 读普通文件时,在读到要求字节数之前已到达了文件尾端
    • 当从终端设备读时,通常一次最多读一行
    • 当从网络读时,网络中的缓冲机构可能造成返回值小于所要求读的字节数
    • 某些面向记录的设备,例如磁带,一次最多返回一个记录
    • 进程由于信号造成中断
  • 读操作从文件的当前位移量处开始,在成功返回前,该位移量增加实际读得的字节数

3.2.5 write 函数--- 写文件

  write(将数据写入已打开的文件内)

 #include<unistd.h>
ssize_t read(int fd, void * buf, size_t count);
  • * 相关函数 open,read,fcntl,close,lseek,sync,fsync,fwrite
  • * 函数说明
    • write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。当然,文件读写位置也会随之移动。
    • write 出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制
    • 对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了 O_APPEND 选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。
  • * 返回值
    • 如果顺利write()会返回实际写入的字节数。当有错误发生时则返回-1,错误代码存入errno中。
    • 返回值通常与参数 count 的值不同,如果不一样则表示出错
  • * 错误代码
    • EINTR 此调用被信号所中断
    • * EAGAIN 当使用不可阻断 I/O 时(O_NONBLOCK),若无数据可读取则返回此值
    • * EBADF 参数 fd 非有效的文件描述词,或该文件已关闭

3.2.6 lseek 函数--- 文件定位

  #include<sys/types.h>
#include<unistd.h>
off_t lseek(int fildes,off_t offset ,int whence);
  • * 相关函数 dup,open,fseek
  • 函数功能:
    • 定位一个已打开的文件  
  • * 函数说明
    • 每一个已打开的文件都有一个读写位置,当打开文件时通常其读写位置是指向文件开头,若是以附加的方式打开文件(如O_APPEND),则读写位置会指向文件尾。当read()或write()时,读写位置会随之增加,lseek()便是用来控制该文件的读写位置。
  • * 参数:
    • @fildes 已打开的文件描述符
    • * @offset 位移量。根据参数whence来移动读写位置的位移数。
    • @ whence  定位的位置,为下列其中一种:
      • * SEEK_SET 将该文件的位移量设置为距离文件开始处 offset  个字节
      • * SEEK_CUR 将该文件的位移量设置为其当前值处 加offset  个字节,offset 可为正或负
      • * SEEK_END 将该文件的位移量设置为文件长度 加offset  个字节,offset 可为正或负
      • * 当whence 值为SEEK_CUR 或SEEK_END时,参数offet允许负值的出现
  • * 返回值
    • 若成功则返回新的文件位移量(绝对偏移量),若出错返回-1.
  • * 特别使用方式
    • * 1) 欲将读写位置移到文件开头时:lseek(int fildes,0,SEEK_SET);
    • * 2) 欲将读写位置移到文件尾时:lseek(int fildes,0,SEEK_END);
    • * 3) 想要取得目前文件位置时:lseek(int fildes,0,SEEK_CUR);
  • * 附件说明
    • Linux系统不允许lseek()对tty装置作用,此项动作会令lseek()返回ESPIPE。
  • 其他使用方式:
    • lseek 也可用来确定所涉及的文件是否可以设置位移量。如果文件描述符引用的是一个管道或FIFO,则 lseek 返回 -1,并将 errno 设置为 EPIPE
    • 每个打开文件都由一个与其相关联的“当前文件偏移量”。它是一个非负整数,用以度量从文件开始处计算的字节数。通常,读、写操作都从当前文件偏移量处开始,并使偏移量增加所读或写的字节数。按系统默认,当打开一个文件时,除非指定 O_APPEND 选择项,否则该位移量被设置为0    

3.3 例子

3.3.1 文件读写

  io.h

 #ifndef __IO_H__
#define __IO_H__ extern void copy(int fdin, int fdout); #endif

  io.c

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "io.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> #define BUFFER_LEN 1024 /* 文件的读写拷贝 */
void copy(int fdin, int fdout)
{
char buff[BUFFER_LEN];
ssize_t size; while((size = read(fdin, buff, BUFFER_LEN)) > ) { //从 fdin 中读取 BUFFER_LEN 个字节存放入 buff 中
if(write(fdout, buff, size) != size) {
fprintf(stderr, "write error: %s\n", strerror(errno));
exit();
}
}
if(size < ) {
fprintf(stderr, "read error:%s\n", strerror(errno));
exit(); // 相当于 return 1;
}
}

  将 io.c 编译成 .o 文件,供其他模块进行调用

 gcc -o obj/io.o -Iinclude -c src/io.c
  • -o:指定输出的目录和文件格式
  • -Iinclude:指定包含的头文件目录,-I使指定包含头文件,后面的inlcude 即是头文件所在的目录,也可以采用绝对路径
  • -c:指定源文件

  cp.c

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "io.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> int main(int argc, char *argv[])
{
if(argc != ) {
fprintf(stderr, "usage: %s srcfile destfile\n", argv[]);
exit();
} int fdin;
int fdout; //打开一个待读取的文件
fdin = open(argv[], O_RDONLY);
if(fdin < ) {
fprintf(stderr, "open error: %s\n", strerror(errno));
exit();
} else {
printf("open file: %d\n", fdin);
} //打开一个待写入的文件
fdout = open(argv[], O_WRONLY | O_CREAT | O_TRUNC, );
if(fdout < ) {
fprintf(stderr, "open error: %s\n", strerror(errno));
exit();
} else {
printf("open file: %d\n", fdout);
} //文件复制
copy(fdin, fdout); close(fdin);
close(fdout); return ;
}

  编译:

 gcc -o bin/cp -Iinclude obj/io.o src/cp.c

  运行:

  三、文件IO——系统调用(续)

  三、文件IO——系统调用(续)

  三、文件IO——系统调用(续)

  三、文件IO——系统调用(续)

3.3.2 lseek 文件定位

(1)例子1

  打印要读取的文件的总的大小

  每次写完后,文件定位的位置

  io.c

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "io.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> #define BUFFER_LEN 1024 /* 文件的读写拷贝 */
void copy(int fdin, int fdout)
{
char buff[BUFFER_LEN];
ssize_t size; // printf("file length: %ld\n", lseek(fdin, 0L, SEEK_END));//将文件定位到文件尾部,偏移量为0L
// lseek(fdin, 0L, SEEK_SET);// 定位到文件开头 while((size = read(fdin, buff, BUFFER_LEN)) > ) { //从 fdin 中读取 BUFFER_LEN 个字节存放入 buff 中
printf("current: %ld\n", lseek(fdin, 0L, SEEK_CUR)); //打印当前位置 if(write(fdout, buff, size) != size) {
fprintf(stderr, "write error: %s\n", strerror(errno));
exit();
}
}
if(size < ) {
fprintf(stderr, "read error:%s\n", strerror(errno));
exit(); // 相当于 return 1;
}
}

  cp.c

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "io.h"
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> int main(int argc, char *argv[])
{
if(argc != ) {
fprintf(stderr, "usage: %s srcfile destfile\n", argv[]);
exit();
} int fdin;
int fdout;
off_t ret; //打开一个待读取的文件
fdin = open(argv[], O_RDONLY);
if(fdin < ) {
fprintf(stderr, "open error: %s\n", strerror(errno));
exit();
} else {
printf("open file: %d\n", fdin);
} 32 ret = lseek(fdin, 0L, SEEK_END); //将文件定位到文件末尾
33 if(ret == -1) {
34 fprintf(stderr, "lseek error: %s\n", strerror(errno));
35 exit(1);
36 }
37 printf("file length: %ld\n", ret);
38
39 ret = lseek(fdin, 0L, SEEK_SET);// 定位到文件开头
40 if(ret == -1) {
41 fprintf(stderr, "lseek error: %s\n", strerror(errno));
42 exit(1);
43 } //打开一个待写入的文件
fdout = open(argv[], O_WRONLY | O_CREAT | O_TRUNC, );
if(fdout < ) {
fprintf(stderr, "open error: %s\n", strerror(errno));
exit();
} else {
printf("open file: %d\n", fdout);
} //文件复制
copy(fdin, fdout); close(fdin);
close(fdout); return ;
}

  编译:

 gcc -o bin/cp -Iinclude src/io.c src/cp.c

  执行:

  三、文件IO——系统调用(续)

(2)空洞文件制作

  空洞文件就是从文件尾部跳出若干个字节再写入信息,中间空出来的信息就是一个空洞

  hole_file.c

 #include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h> //生成空洞文件
char *buff = ""; int main(int argc, char *argv[])
{
if(argc < ) {
fprintf(stderr, "usage: %s [file]\n", argv[]);
exit();
} int fd;
size_t size; fd = open(argv[], O_WRONLY | O_CREAT | O_TRUNC, );
if(fd < ) {
perror("open error");
exit();
} // 写入多少个字节,一个字符串占多少个字节
size = strlen(buff) * sizeof(char); // 将字符串写入到空洞文件中
if(write(fd, buff, size) != size) {
perror("write error");
exit();
} // 定位到文件尾部的 10 个字节处
if(lseek(fd, 10L, SEEK_END) == -) {
perror("lseek error");
exit();
} // 从文件尾部的10个字节处再写入字符串
if(write(fd, buff, size) != size) {
perror("write error");
exit();
} close(fd);
return ; }

  编译:

 gcc -o bin/hole_file src/hole_file.c

  执行:

  三、文件IO——系统调用(续)