【Linux】系统编程之文件(常用API)

一、文件描述符

在Linux操作系统中的一切都被抽象成了文件,那么一个打开的文件是如何与应用程序进行对应呢?
解决方案是使用文件描述符(file descriptor,简称fd),当在进程中打开一个现有文件或者创建一个新文件时,内核向该进程返回一个文件描述符,用于对应这个打开/新建的文件。这些文件描述符都存储在内核为每个进程维护的一个文件描述符表中。
在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

1、对于内核而言,所有打开文件都由文件描述符引用。文件描述符是一个非负整数。当打开一个现存文件或者创建一个新文件时,内核向进程返回一个文件描述符。当读写一个文件时,用open和creat返回的文件描述符标识该文件,将其作为参数传递给read和write。
按照惯例,UNIX系统shell使用文件描述符0与进程的标准输入关联,文件描述符1与标准输出关联,文件描述符2与标准错误输出关联。STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO这几个宏代替了0、1、2这几个魔数,用来提高可读性,头文件为<unistd.h>。(补充里有简单示例
2、文件描述符,这个数字在一个进程中表示一个特定含义,当我们open一个文件时,操作系统在内存中构建了一些数据结构来表示这个动态文件,然后返回给应用程序一个数字作为文件描述符,这个数字就和我们内存中维护的这个动态文件的这些数据结构绑定上了,以后我们应用程序如果要操作这个动态文件,只需要用这个文件描述符区分。
3、文件描述符的作用域就是当前进程,出了这个进程文件描述符就没有意义了。
open函数打开文件,打开成功返回一个文件描述符,打开失败,返回-1。

在这里插入图片描述
这张图片引用@爱编程的大丙图片出处

二、函数open和creat

1、函数open

调用open函数可以打开或创建一个文件。
函数原型:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
open是一个系统函数, 只能在linux系统中使用, windows不支持
fopen 是标准c库函数, 一般都可以跨平台使用, 可以这样理解:
		- 在linux中 fopen底层封装了Linux的系统API open
		- 在window中, fopen底层封装的是 window 的 api
*/
// 打开一个已经存在的磁盘文件
int open(const char *pathname, int flags);
// 打开磁盘文件, 如果文件不存在, 就会自动创建
int open(const char *pathname, int flags, mode_t mode);

参数介绍:

pathname:要打开的文件名(含路径,缺省为当前路径)
flags:以什么的方式打开,O_RDONLY(只读打开),O_WRONLY(只写打开),O_RDWR(可读可写打开)
mode:打开方式的数字表现形式,例如:0600是代表可读可写的意思;切记mode一定是在flags中使用了O_CREAT标志才能用,mode记录待创建的文件访问权限

切记:O_RDWR(可读可写),O_WRONLY(只写打开),O_RDONLY(只读打开)
当附带上上述权限后,打开的文件只能按照这种权限来操作,以上的三个常数中应当只指定一个,而下面的这些常数是可选择的。
(1)O_CREAT:若文件不存在,则创建它,使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限。
(2)O_EXCL:检测文件是否存在,必须要和 O_CREAT一起使用,不能单独使用;如果同时指定了O_CREAT,文件不存在则创建文件,而文件已经存在,则会出错(即open会返回个-1);注意:如果有O_EXCL的存在的话,write函数是写不进东西的。
(3)O_APPEND:每次写入文件时都加到文件的尾端,即写入文件时,如果加上O_APPEND的话,write会根据文件里的原有内容将新内容接到旧内容尾巴去,如果没加O_APPEND,则会根据新内容的长度覆盖旧内容的对应的长度,覆盖不是删除,如果内容短,则会保留上一次的未被覆盖的内容。
(4)O_TRUNC:以这个属性去打开文件时,如果这个文件本来是有内容的,而且只为只读或只写成功打开后,则将其长度截断为0,也就是将原文件的旧内容全部删掉,写入新的内容。

返回值:

若成功,返回文件描述符;(内核分配的文件描述符,这个值被记录在内核的文件描述符表中,这是一个大于 0 的整数)
若出错,返回-1

示例:
1、打开file1文件,如果没有则创建,并打印文件描述符
代码:

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

int main()
{
	int fd;//file description文件描述符

	fd = open("./file1",O_RDWR);
	
	if(fd == -1){
		printf("open file1 failed!\n");
		fd = open("./file1",O_RDWR|O_CREAT,0600);
		if(fd > 0){
			printf("creat file1 successed!\n");
		}
	}

	printf("fd = %d\n",fd);

	return 0;
}

结果:
在这里插入图片描述
2、O_EXCL测试文件file是否存在
代码:

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

int main()
{
        int fd;//file description文件描述

        fd = open("./file",O_RDWR|O_CREAT|O_EXCL,0600);//O_EXCL测试文件file是否存在,若存在则出错,返回-1

        if(fd == -1){
                printf("文件file存在!\n");
        }

        printf("fd = %d\n",fd);

        return 0;
}

结果:
在这里插入图片描述
3、O_APPEND的用法以及效果(这里提前运用了write和close,自行先看下方介绍)
代码:

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

int main()
{
        int fd;//file description文件描述
        char *buf = "Apibro is very nice!";

        fd = open("./file1",O_RDWR|O_APPEND|O_CREAT,0600);//加上O_APPEND,在文件尾端追加,不加则覆盖掉,并不是删除   

        printf("open file1 successed : fd = %d\n",fd);
        
        //ssize_t write(int fd, const void *buf, size_t count);写函数原型
        int n_write = write(fd,buf,strlen(buf));//sizeof()计算指针长度,strlen()计算字符串长度,sizeof(buf)最多8个字节,所以这里用strlen
        if(n_write != -1){
                printf("write %d bytes to file1\n",n_write);
        }

        close(fd);

        return 0;
}

注意:sizeof计算出的是指针buf的大小,strlen计算的才是buf内容的有效长度
sizeof()计算指针长度,strlen()计算字符串长度

结果:
在这里插入图片描述
file1,这里原来写的是上面这一句【abcdefghijklmnopqrstuvwxyz这里面写的是26个字母】
加上O_APPEND,如下:
在这里插入图片描述
不加O_APPEND,如下:
在这里插入图片描述
4、O_TRUNC的用法以及效果(这里提前运用了write和close,自行先看下方介绍)
代码:

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

int main()
{
        int fd;//file description文件描述
        char *buf = "Apibro is very nice!";

        fd = open("./file1",O_RDWR|O_TRUNC|O_CREAT,0600);//加上O_TRUNC,会将原来的文件干掉,文件长度截断为0,重新写入
        printf("open file1 successed : fd = %d\n",fd);

        //ssize_t write(int fd, const void *buf, size_t count);写函数原型
        int n_write = write(fd,buf,strlen(buf));//sizeof()计算指针长度,strlen()计算字符串长度,sizeof(buf)最多8个字节,所以这里用strlen
        if(n_write != -1){
                printf("write %d bytes to file1\n",n_write);
        }

        close(fd);

        return 0;
}

结果:
file1,这里原来写的是上面这一句【abcdefghijklmnopqrstuvwxyz这里面写的是26个字母】
加上O_TRUNC,如下:
在这里插入图片描述

2、函数creat

调用creat函数可创建一个新文件。
函数原型:

       #include <fcntl.h>
       int creat(const char *pathname, mode_t mode);

参数介绍:

pathname:要创建的文件名(包含路径,缺省为当前路径)
mode:创建模式(可读可写可执行)

常见的创建模式(mode):

宏表示数字
S_IRUSR4可读
S_IWUSR2可写
S_IXUSR1可执行
S_IRWXU7可读、写、执行

返回值:

若成功,返回为只写打开的文件描述符;
若出错,返回-1

示例:
代码:

#include <fcntl.h>

int main()
{
        int fd;//file description文件描述
        
        //int creat(const char *pathname, mode_t mode);
        fd = creat("/home/apibro/SYSTEM_PRO/FILE/creatfile_demo");//路径
        
        return 0;
}

三、函数close

可调用一个close函数关闭一个打开文件。
函数原型:

       #include <unistd.h>
       int close(int fd);

参数介绍:

fd:文件描述符

返回值:

若成功,返回0;
若失败,返回-1

四、函数write、read和lseek

1、函数write

调用write函数向打开文件写入数据
作用:将缓冲区buf中的count个字节的数据写入文件中(fd指向的文件)
函数原型:

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

参数介绍:

fd:文件描述符
buf:指定存储器写入数据的缓冲区
count:指定写入的字节数(指定写入文件的大小)

返回值:

若成功,返回已写字节数;int n_write = write()
若出错,返回-1

2、函数read

调用read函数从打开文件中读数据
作用:把文件(fd指向的文件)中,读取的count个字节的数据,放到缓冲区buf中
函数原型:

#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);

参数介绍:

fd:文件描述符
buf:指定存储器读出数据的缓冲区
count:指定读出的字节数

返回值:

若成功,返回读到的字节数,int n_read = read() ,已到文件尾,返回0(未读到)
若出错,返回-1

3、函数lseek

可调用lseek显示地为一个打开文件设置偏移量(光标的操作)
函数原型:

#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);

参数介绍:

fd:文件描述符
offset:偏移量,对whence的偏移量,单位字节,可正可负(向前移,向后移)
whence:固定点位置,当前位置的基点

对参数offset的解释与参数whence的值有关:
(1)若whence是SEEK_SET:文件头的位置,则将光标移至距离文件开始处offset个字节。
(2)若whence是SEEK_CUR:光标当前的位置,则将光标移至距离光标当前处offset个字节,可正可负。
(3)若whence是SEEK_END:文件尾巴的位置,则将光标移至距离文件尾offset个字节(偏移量设置为文件长度加减offset,后面补充文件长度计算)。

返回值:

若成功,返回新的文件偏移量offset ,新文件光标位置
即返回目前的读写位置,也就是距离文件开头多少个字节
若出错,返回-1

示例:
综合open、close、write、read、lseek的示例。
代码:

#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 fd;//file description文件描述
        char *buf = "Apibro is very nice!";

        fd = open("./file1",O_RDWR);

        if(fd == -1){
                printf("open file1 failed!\n");
                fd = open("./file1",O_RDWR|O_CREAT,0600);
                if(fd > 0){
                        printf("creat file1 successed!\n");
                }
        }

        printf("open file1 successed : fd = %d\n",fd);

        //ssize_t write(int fd, const void *buf, size_t count);写函数原型
        int n_write = write(fd,buf,strlen(buf));//sizeof()计算指针长度,strlen()计算字符串长度,sizeof(buf)最多8个字节,所以这里用strlen
        if(n_write != -1){
                printf("write %d bytes to file1\n",n_write);
        }
        
        char *readBuf;//如果不malloc,这就成了野指针
        readBuf = (char *)malloc(sizeof(char)*n_write + 1);//+1可有可无,多加一个字节空间而已

        //off_t lseek(int fd, off_t offset, int whence);光标移动操作函数原型
        lseek(fd,0,SEEK_SET);//光标从开头偏移0字符
        //lseek(fd,-n_write,SEEK_CUR);//光标从当前位置向前偏移n_write个字符
        //lseek(fd,-n_write,SEEK_END);//光标从末尾向前偏移n_write个字符

        //ssize_t read(int fd, void *buf, size_t count);读函数原型
        int n_read = read(fd,readBuf,n_write);

        printf("read %d bytes,context :%s\n",n_read,readBuf);

        close(fd);

        return 0;
} 

结果:
在这里插入图片描述

五、补充

1、lseek函数计算文件长度

lseek可以固定光标位置,设置偏移量,所以可以计算文件的内容长度
示例:
代码:

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

int main()
{
        int fd;//file description文件描述
        char *buf = "Apibro is very nice!";

        fd = open("./file1",O_RDWR);

        int filesize = lseek(fd,0,SEEK_END);
        printf("file's size is : %d\n",filesize);

        close(fd);

        return 0;
}

结果:
在这里插入图片描述

2、文件描述符012(标准输入、输出、错误)

标准化文件描述符fd,简单使用
示例:
代码:

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

int main()
{
        int fd;//file description文件描述
        char readBuf[128];//为了方便直接定了128,可用malloc开辟空间,头文件<stdlib.h>

        //ssize_t read(int fd, void *buf, size_t count);读函数原型
        int n_read = read(0,readBuf,6);

        //ssize_t write(int fd, const void *buf, size_t count);写函数原型
        int n_write = write(1,readBuf,strlen(readBuf));//sizeof()计算指针长度,strlen()计算字符串长度,sizeof(buf)最多8个字节,所以这里用strlen
        
        printf("\ndone!\n");

        return 0;
}

结果:
在这里插入图片描述

最后谢谢阅读,笔者乃小白,如有错误之处还请指正。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Apibro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值