UNIX环境高级编程 - - - 文件编程1

UNIX环境高级编程 - - - 文件编程1


前言

文中描述的函数经常被称为不带缓冲的I/O——打开文件、读文件、写文件等
UNIX系统中的大多数文件I/O都只需要五个函数:open、read、write、lseek、以及close。


一、什么是文件描述符?

概念:对于内核而言,所有打开的文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程放回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传给read和write

UNIX shell使用文件描述符0与进程的标准输入相结合,文件描述符1与标准输出相结合,文件描述符2与标准错误输出相结合。

下面介绍系统的是是三个文件描述符(使用宏本质上是非负整数 0,1,2):

STDIN_FILENO 				文件描述符0						标准输入
STDOUT_FILENO				文件描述符1						标准输出
STDERR_FILENO				文件描述符2						标准错误

注意事项:

  1. 文件描述符的作用域就是当前进程,出了这个进程文件描述符就失效了,只对当前进程有意义
  2. 文件描述符,这个数字,在一个进程中标识一个特定的含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要使用文件描述符区分。

二、文件编程API详解

1、creat函数

函数原型

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int creat(const char *pathname, mode_t mode);

函数作用:创建一个新文件
参数说明:
pathname:要打开或者创建文件的名字或路径。
mode:设置创建的新文件的权限。

返回值:成功返回只写打开的文件描述符;失败返回-1.

2、open函数

函数原型:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);

函数作用:打开或创建一个文件。
参数说明:
pathname:要打开或者创建文件的名字或路径。
flags:配置打开文件后的权限。
mode:设置创建的新文件的权限(mode参数一定是在flags中使用了O_CREAT标志,mode记录待创建的文件权限)

flags参数常用的宏如下:

O_RDONLY——只读打开
O_WRONLY——只写打开
O_RDWR——可读可写

上面三个选其一,下面的宏可搭配上面三个宏使用

O_CREAT——若文件不存在则就创建它(使用时,需搭配mode参数,说明新创建文件的权限)
O_EXCL——如果同时指定O_CREAT,而文件已经存在,就会出错
O_APPEND——每次写入都加到文件的尾端
O_TRUNC——该属性打开文件时,如果文件本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0

函数返回值:成功返回文件描述符;失败返回-1。

代码演示:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main(void)
{
    
    
        int discription;
        discription=open("./file1",O_RDONLY);
		if(discription == -1)
        {
    
    
                printf("打开文件失败\n");
                return 0;
        }

        printf("discription=%d\n",discription);
        
        return 0;
}

函数运行结果:

打开文件失败
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>


int main(void)
{
    
    
        int discr;
        discr=open("./file",O_RDWR);
        if(discr==-1)
        {
    
    
                printf("file is unconsistenace\n");
                discr=open("./file",O_RDWR|O_CREAT,0600);
                if(discr>0)
                {
    
    
                        printf("file is created\n");
                }
        }
	
		close(discr);
        return 0;
}


函数运行结果:

file is unconsistenace
file is created

3、openat函数

函数原型:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int openat(int dirfd, const char *pathname, int flags);
int openat(int dirfd, const char *pathname, int flags, mode_t mode);

函数作用:打开或创建一个文件。
参数说明:
flags:配置打开文件后的权限。
mode:设置创建的新文件的权限(mode参数一定是在flags中使用了O_CREAT标志,mode记录待创建的文件权限)
dirfd:该参数是区分open函数和openat函数的关键,分三种情况:

  1. pathname:为绝对路径名,此时可以忽略dirfd,openat函数就相当于open函数
  2. pathname:为相对路径名,dirfd 参数指出了相对路径名在文件系统中的开始位置。dirfd 是通过打开相对路径名所在目录来获取
  3. pathname:为相对路径名,,dirfd 参数具有特殊值AT_FDCWD。此时路径名在当前目录获取,openat()和open()在操作上类似

函数返回值:成功返回文件描述符;失败返回-1。

4、close函数

函数原型:

#include <unistd.h>

int close(int fd);

函数作用:关闭一个已经打开的文件。
参数说明:
fd:文件描述符,open,openat返回的文件描述符。

函数返回值:成功返回0;失败返回-1。


5、lseek函数

函数原型:

#include <unistd.h>

off_t lseek(int fd,off_t offest,int whence);

函数作用:显式的为一个打开的文件设置偏移量。
参数说明:
fd:文件描述符。
offest:为偏移量。
whence:表示从文件哪里开始偏移。系统中有三个宏,来控制该参数。

SEEK_SET		文件的偏移量设置为从文件头部开始偏移,偏移offest个字节。
SEEK_CUR		文件的偏移量设置为从文件的当前位置开始偏移,偏移offest个字节,可负可正。
SEEK_END		文件的偏移量设置为从文件的末尾位置开始偏移,偏移offest个字节,可负可正。

函数返回值:成功返回文件偏移量,失败返回-1;

6、read函数

函数原型:

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);

函数作用:从打开的文件中读取数据。
参数说明:
fd:文件描述符。
buf:把文件中读出的数据存放在buf中。
count:读取多少个字节的数据。

函数返回值:成功返回读取的字节数,若读到文件末尾,返回0,若失败,返回-1。

注意事项:有以下几种情况,可以使实际读到的字节数少于要求读到的字节数。

  1. 读普通文件时,在读到要求直接输之前已经达到文件的尾端。
  2. 当从终端设备读取时,通常一次最多读一行
  3. 当从网络中读取时,网络中的缓冲机制可能造成返回值小于所需数量,read将返回实际读取到的字节数。
  4. 当从管道或者FIFO中读时,若管道包含的字节善于所需的数量,read将返回实际读取到的字节数。
  5. 当从某些面向记录的设备读时,一次最多返回一个记录。
  6. 当一信号造成中断,而已经读取了部分数据量时。

7、write函数

函数原型:

#include <unistd.h>

ssize_t write(int fd, const void *buf, size_t count);

函数作用:向打开的文件中写入数据。
参数说明:
fd:文件描述符。
buf:把要写入到文件中的数据存放在buf中。
count:写入多少个字节的数据。

函数返回值:成功返回写入的字节数,若失败,返回-1。

案例展示:

  1. 使用上述API实现从文件中读取/写入结构体的操作
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include<stdlib.h>

struct STU
{
    
    
        int score;
        char name;
};


int main(int argc,char **argv)
{
    
    
        if(argc != 2)
        {
    
    
                perror("main:");
                return -1;
        }

        int fd;
        ssize_t err;
        struct STU STU1={
    
    100,'a'};

        fd=open(argv[1],O_RDWR);
        if(fd == -1)
        {
    
    
                perror("open:");
                return -1;
        }

        err =  write(fd,&STU1,sizeof(struct STU));
        if(err == -1)
        {
    
    
                perror("write:");
                return -1;
        }

        err = read(fd,&STU1,sizeof(struct STU));
        if(err == -1)
        {
    
    
                perror("read:");
                return -1;
        }

        close(fd);
        printf("score:%d,name:%c\n",STU1.score,STU1.name);


        return 0;

}
运行结果:

```c
score:10,name:a

//打开文件,可以看到文件中被写入了内容。

2.利用上述API,实现配置文件的修改(修改文件中的内容为字符)。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
    
    
        int fdSrc;
        int  err;
        char *buf=NULL;

        if(argc!=2)
        {
    
    
                perror("main:");
                return -1;
        }

        fdSrc = open(argv[1],O_RDWR);
        if(fdSrc == -1)
        {
    
    
                perror("open:");
                return -1;
        }

        err = lseek(fdSrc,0,SEEK_END);
        if(err == -1)
        {
    
    
                perror("lseek:");
                return -1;
        }

        buf=(char *)malloc(sizeof(char)*err+8);
        if(buf == NULL)
        {
    
    
                perror("malloc:");
                return -1;
        }
        
		lseek(fdSrc,0,SEEK_SET);

        err= read(fdSrc,buf,err);
        if(err == -1)
        {
    
    
                perror("read:");
                return -1;
        }

        char *p=strstr(buf,"length=");
        if(p == NULL)
        {
    
    
                printf("not found\n");
                exit(-1);
        }
        p=p+strlen("length=");
        *p='2';
        p++;
        *p='5';
        p++;
        *p='1';

        close(fdSrc);
        fdSrc=open(argv[1],O_RDWR);
        if(fdSrc==-1)
        {
    
    
                perror("open:");
                return -1;
        }
        err = write(fdSrc,buf,strlen(buf));
        if(err == -1)
        {
    
    
                perror("write:");
                return -1;
        }

        close(fdSrc);
        return 0;
}

运行结果:

//运行前文件中的内容为:
/*
		hight=10
		length=110
		width=200
*/
//运行后文件中的内容为:
/*
		hight=10
		length=251
		width=200
*/

3.利用上述API实现,系统中cp指令。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

int main(int argc,char **argv)
{
    
    
        int fdSrc;
        int fdDes;
        int size;
        char *buf=NULL;

        if(argc!=3)
        {
    
    
                printf("please input curent parameter\n");
                return -1;
        }

        fdSrc = open(argv[1],O_RDWR);
        if(fdSrc == -1)
        {
    
    
                perror("openSrc:");
                return -1;
        }

        size = lseek(fdSrc,0,SEEK_END);
        if(size == -1)
        {
    
    
                perror("lseek:");
                return -1;
        }

        buf = (char *)malloc(sizeof(char)*size+8);
        if(buf == NULL)
        {
    
    
                perror("malloc:");
                return -1;
        }
        int err = lseek(fdSrc,0,SEEK_SET);
        if(err == -1)
        {
    
    
                perror("lseek:");
                return -1;
        }

        int n_read = read(fdSrc,buf,size);
        if(n_read == -1)
        {
    
    
                perror("read:");
                return -1;
        }

        fdDes=open(argv[2],O_RDWR|O_CREAT|O_TRUNC,0600);
        if(fdDes == -1)
        {
    
    
                perror("openDes:");
                return -1;
        }

        err = write(fdDes,buf,strlen(buf));
        if(err == -1)
        {
    
    
                perror("write:");
                return -1;
        }


        close(fdSrc);
        close(fdDes);
        return 0;
}


运行结果:

/*  ./demo5 test2.txt test3.txt
查看是否创建test3.txt,并确认是否和test2.txt内容相同
笔者亲测可行。
*/

三、标准C库对文件的操作与UNIX文件的操作

1、标准C库的文件操作

包含头文件 #include <stdio.h>

可以使用标砖C库对文件操作的API,有fopen()、getc()、putc()、exit()、fclose()、fprintf()、fscanf()、fgets()、fputs()、fseek()、fread()、fwrite()、feof()、fflush()等等

标准C库对文件的操作:本质上就是对文件的操作(与文件进行通信)。

2、I/O级别

I/O有两个级别:

  1. 底层I/O:使用操作系统提供的基本I/O服务。
  2. 标准高级I/O:使用C库的标准包和 stdio.h 头文件定义。

3、标准C库与UNIX系统底层I/O对文件的操作区别

注意笔者这里以 open() 和 fopen() 为例。

  1. open() 是系统调用 返回的是文件句柄,文件的句柄是文件在文件描述副表里的索引,fopen是C的库函数,返回的是一个文件指针。
  2. 标准I/O有许多专门的函数简化了处理不同的问题。
  3. 标准I/O都是有缓冲的,系统调用没有。
  4. open() 和read()、write()配合使用,fopen() 和fread()、fwrite()配合使用。
  5. fopen() 是在open()的基础是哪个扩展出来的(也就是封装),在大多数情况下选用标准C库的I/O操作,可以提高移植性。
  6. open() 是底层I/O,fopen() 是标准高级I/O。

4、扩展一下标准C库的fopen()打开是常用的权限

对于标准C库文件操作的API和Linux下的使用的方法相似,但是需要注意的是打开文件的权限

模式名称 读写权限 文件是否存在?
r 只读不写
w 只写不读 否,若不存在则自动创建空文件;写操作会从文件头部写入数据
a 只写不读 否,若不存在则自动创建空文件;写操作会从文件尾部写入数据
r+ 可读可写
a+ 可读可写 存不存在都可以,若不存在则自动创建空文件;写操作会从文件尾部写入数据
w+ 可读可写 存不存在都可以,若不存在则自动创建空文件;写操作会从文件头部写入数据

四、缓冲文件系统和非缓冲文件系统

1、缓冲文件系统

缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”, 装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。由此可以看出,内存 “缓冲区”的大小,影响着实际操作外存的次数,内存“缓冲区”越大,则操作外存的次数就少,执行速度就快、效率高。一般来说,文件“缓冲区”的大小随机器 而定。
fopen, fclose, fread, fwrite, fgetc, fgets, fputc, fputs, freopen, fseek, ftell, rewind等

2、非缓冲文件系统

缓冲文件系统是借助文件结构体指针来对文件进行管理,通过文件指针来对文件进行访问,既可以读写字符、字符串、格式化数据,也可以读写二进制数 据。非缓冲文件系统依赖于操作系统,通过操作系统的功能对文件进行读写,是系统级的输入输出,它不设文件结构体指针,只能读写二进制文件,但效率高、速度 快,由于ANSI标准不再包括非缓冲文件系统,因此建议大家最好不要选择它。
open, close, read, write, getc, getchar, putc, putchar 等。

参考资料:
《UNIX环境高级编程》第三版
《C primer plus》第六版

Guess you like

Origin blog.csdn.net/weixin_51363326/article/details/116275911