Linux系统编程——文件编程

1.打开创建/文件

open函数

  open 的返回值是一个文件描述符,对于文件的后续操作比如读/写(read/write)都需要使用到它。它实际上是一个索引,我们在程序中打开文件后内核都会建立一个结构体来管理这些文件而文件描述符会指向内核里的这些结构体起到一个索引作用。
在这里插入图片描述

参数说明

使用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:
  O_RDONLY 只读打开  O_WRONLY 只写打开  O_RDWR 可读可写打开
当我们附带了权限后,打开的文件就只能按照这种权限来操作。
以上这三个常数中应当只指定一 个。下列常数是可选择的:
  O_CREAT 若文件不存在则创建它。使用此选项时,需要同时说明第三个参数mode,用其说明该新文件的存取许可权限。
  O_EXCL 如果同时指定了OCREAT,而文件已经存在,则出错。
  O_APPEND 每次写时都加到文件的尾端。
  O_TRUNC 属性去打开文件时,如果这个文件中本来是有内容的,而且为只读或只写成功打开,则将其长度截短为0。
Mode:一定是在flags中使用了O_CREAT标志,mode记录待创建的文件的访问权限

demo1:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
   
int main()
{
           int fd;
   
          fd = open("./file1",O_RDWR);
  
          printf("fd = %d\n",fd);
  
          return 0;
}

文件打开成功返回:
在这里插入图片描述
打开失败返回:
在这里插入图片描述

demo2:文件不存在我们让它自动创建
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main()
{
        int fd;

        fd = open("./file1",O_RDWR);
        if(fd < 1){
                printf("open file1 fialed\n");
                fd = open("./file1",O_RDWR|O_CREAT,0600);//O_CREAT可以让代码创建一个文件,0600 是这个文件的权限
                if(fd > 0){
                        printf("create file1 success\n");
                }
        }

        return 0;
}

在这里插入图片描述

0600 的意思:

文件详细信息
在这里插入图片描述
对0600 的解读
在这里插入图片描述

2.读/写文件

write 函数(写文件)

#include <unistd.h>

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

返回值:执行成功返回整型数(写入的个数)
fd:open 返回的文件描述符
buf:缓冲区,可理解为写入的内容的存放的地方
count:写入文件的大小

demo3:向文件写入内容
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

	fd = open("./file1",O_RDWR);
	if(fd < 1){
		printf("open file1 fialed\n");
		fd = open("./file1",O_RDWR|O_CREAT,0600);
		if(fd > 0){
			printf("create file1 success\n");
		}
	}

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

	write(fd,buf,strlen(buf));

	close(fd);

	return 0;
}

在这里插入图片描述
!!!指针在Linux 环境中占8字节

read 函数(读文件)

#include <unistd.h>

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

返回值:执行成功返回读取到的字节数,读了多少返回多少,读到文件尾啥都没读到就返回0,出错返回 -1
fd:open 返回的文件描述符
buf:缓冲区,可理解为读取的内容的存放的地方,从fd 指向的文件读取count 个字节到buf 里来
count:读取文件的大小

demo4:读取文件内容
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

	fd = open("./file1",O_RDWR);
	if(fd < 1){
		printf("open file1 fialed\n");
		fd = open("./file1",O_RDWR|O_CREAT,0600);
		if(fd > 0){
			printf("create file1 success\n");
		}
	}

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

	int n_write = write(fd,buf,strlen(buf));
	if(n_write != -1){
		printf("write %d byte to file1\n",n_write);
	}

	char *readBuf;
	readBuf = (char *)malloc(sizeof(char)*n_write +1);
	int n_read = read(fd,readBuf,n_write);

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

	close(fd);

	return 0;
}

这个时候我们运行结果会失败,会读不到东西
在这里插入图片描述
因为我们在写文件的时候光标移动到了最后,读的时候是从光标的位置开始读(也就是最后)所以什么都读不到
在这里插入图片描述

demo5:读取文件内容
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

	fd = open("./file1",O_RDWR);
	if(fd < 1){
		printf("open file1 fialed\n");
		fd = open("./file1",O_RDWR|O_CREAT,0600);
		if(fd > 0){
			printf("create file1 success\n");
		}
	}

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

	int n_write = write(fd,buf,strlen(buf));
	if(n_write != -1){
		printf("write %d byte to file1\n",n_write);
	}
	
	close(fd);

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

	char *readBuf;
	readBuf = (char *)malloc(sizeof(char)*n_write +1);
	int n_read = read(fd,readBuf,n_write);

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

	close(fd);

	return 0;
}

在这里插入图片描述

3.文件光标移动操作

lseek函数

#include <sys/types.h>
#include <unistd.h>

   off_t lseek(int fd, off_t offset, int whence);
参数说明

fd:open 返回的文件描述符
offset:偏移值
whence:光标所在的位置,有三个参数,SEEK_SET 光标会移动到文件开头,文件偏移量为偏移量字节(offset)。
                  SEEK_END 光标会移动到文件结尾,文件偏移量为文件大小加上偏移量字节(offset)。
                  SEEK_CUR 光标在上次插入的最后位置,文件偏移量为其当前位置加上偏移量字节(offset)。

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

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

	fd = open("./file1",O_RDWR);
	if(fd < 1){
		printf("open file1 fialed\n");
		fd = open("./file1",O_RDWR|O_CREAT,0600);
		if(fd > 0){
			printf("create file1 success\n");
		}
	}

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

	int n_write = write(fd,buf,strlen(buf));
	if(n_write != -1){
		printf("write %d byte to file1\n",n_write);
	}
	
	lseek(fd,0,SEEK_SET);	

	char *readBuf;
	readBuf = (char *)malloc(sizeof(char)*n_write +1);
	int n_read = read(fd,readBuf,n_write);

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

	close(fd);

	return 0;
}

在这里插入图片描述

demo7:利用lseek 计算文件的大小
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

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

	
	int filesize = lseek(fd,0,SEEK_END);	

	printf("file size:%d\n",filesize);

	close(fd);

	return 0;
}

在这里插入图片描述

在这里插入图片描述

4.文件打开创建的补充

Flags 的其他三个参数讲解

open函数 flags 的参数 O_EXCL

用于判断文件是否存在

demo8:利用O_EXCL 判断文件是否存在
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

int main()
{
	int fd;

	fd = open("./file1",O_RDWR|O_CREAT|O_EXCL,0600);
	if(fd == -1){
		printf("file1 cun zai\n");
	}

	return 0;
}

在这里插入图片描述
运行前没有file1文件会自动创建文件,而如果file1 存在就会出错(open返回-1)
在这里插入图片描述

open函数 flags 的参数 O_APPEND

用于写入时,每次写入都在文件尾写入
file1 原内容
在这里插入图片描述
不用flags 的O_APPEND 属性写,file1 的内容,它会覆盖前面写的内容
在这里插入图片描述
用flags 的O_APPEND 属性写,它会另起一行
在这里插入图片描述

demo9:利用O_APPEND写入
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

	fd = open("./file1",O_RDWR|O_APPEND);
	if(fd < 1){
		printf("open file1 fialed\n");
	}


	write(fd,buf,strlen(buf));


	return 0;
}
open函数 flags 的参数 O_TRUNC

加了flags 的O_TRUNC 属性后打开文件会将文件原内容截为0
file1 原内容
在这里插入图片描述
加入O_TRUNC 写入内容后
在这里插入图片描述

demo10:利用O_TRUNC写入
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
	int fd;
	char *buf = "*** hen shuai!";

	fd = open("./file1",O_RDWR|O_TRUNC);
	if(fd < 1){
		printf("open file1 fialed\n");
	}


	write(fd,buf,strlen(buf));


	return 0;
}
creat函数

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

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

在这里插入图片描述

demo11:利用creat 函数创建文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main()
{
	int fd;

	fd = creat("./file2",S_IRWXU);//创建的文件可读可写可执行

	return 0;
}

在这里插入图片描述
在这里插入图片描述

6.文件操作原理简述

文件描述符

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

demo12:使用标准输入/输出
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fd;

	char readBuf[64];

	int n_read = read(0,readBuf,5);// 标准输入对应于我们得键盘能够使用键盘输入内容

	int n_write = write(1,readBuf,strlen(readBuf));// 标准输出,相当于printf的打印

	printf("\n");

	return 0;
}

2 对应标准错误,比如说文件出错了不喜欢打印,就可以放在标准错误里,特别是以后项目做大以后输出的东西特别多,这个时候使用2 会方便一点。

为什么文件的操作是:打开文件 -> 读/写文件 -> 关闭文件

1、在Linux中要操作一个文件,一般是先open打开一个文件,得到文件描述符,然后对文件进行读写操作(或其他操作),最后是close关闭文件即可。

2、强调一点:我们对文件进行操作时,一定要先打开文件,打开成功之后才能操作,如果打开失败,就不用进行后边的操作了,最后读写完成后,一定要关闭文件,否则会造成文件损坏。

3、文件平时是存放在块设备中的文件系统文件中的,我们把这种文件叫静态文件,当我们去open打开一个文件时,linux内核做的操作包括:内核在进程中建立一个打开文件的数据结构,记录下我们打开的这个文件;内核在内存中申请一段内存,并且将静态文件的内容从块设备中读取到内核中特定地址管理存放(叫动态文件)。
在这里插入图片描述

4、打开文件以后,以后对这个文件的读写操作,都是针对内存中的这一份动态文件的,而并不是针对静态文件的。当然我们对动态文件进行读写以后,此时内存中动态文件和块设备文件中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。

5、为什么这么设计,不直接对块设备直接操作。

块设备本身读写非常不灵活,是按块读写的,而内存是按字节单位操作的,而且可以随机操作,很灵活。

7.写结构体、数组到文件

写一个整数到文件
demo16:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
	int fd;
	int data =100;
	int data2 = 0;

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

	int n_write = write(fd,&data,sizeof(int));

	lseek(fd,0,SEEK_SET);

	int n_read = read(fd,&data2,sizeof(int ));

	printf("read:%d \n",data2);

	close(fd);

	return 0;
}
写一个结构体到文件
demo17:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

struct Test{
	int a;
	char c;
};

int main()
{
	int fd;
	struct Test data = {10,'a'};
	struct Test data2;

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

	int n_write = write(fd,&data,sizeof(struct Test));

	lseek(fd,0,SEEK_SET);

	int n_read = read(fd,&data2,sizeof(struct Test));

	printf("read:%d,%c \n",data2.a,data2.c);

	close(fd);

	return 0;
}
写一个结构体数组/链表到文件
demo18:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>

struct Test{
	int a;
	char c;
};

int main()
{
	int fd;
	struct Test data[2] = {{10,'a'},{101,'b'}};
	struct Test data2[2];

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

	int n_write = write(fd,&data,sizeof(struct Test)*2);
						//如果是链表的话需要做一点小算法,一次写一个过去

	lseek(fd,0,SEEK_SET);

	int n_read = read(fd,&data2,sizeof(struct Test)*2);


	printf("read:%d,%c \n",data2[0].a,data2[0].c);
	printf("read:%d,%c \n",data2[1].a,data2[1].c);

	close(fd);

	return 0;
}

8.标准C库对文件的操作

open与fopen 函数的区别
1.来源
  • open 是UNIX系统调用函数(包括LINUX等),返回的是文件描述符(File Descriptor),它是文件在文件描述符表里的索引。
  • fopen 是ANSIC标准中的C语言库函数,在不同的系统中应该调用不同的内核api。返回的是一个指向文件结构的指针。
2.移植性

这一点从上面的来源就可以推断出来,fopen是C标准函数,因此拥有良好的移植性;而open是UNIX系统调用,移植性有限。如windows下相似的功能使用API函数CreateFile

3.适用范围
  • open返回文件描述符,而文件描述符是UNIX系统下的一个重要概念,UNIX下的一切设备都是以文件的形式操作。如网络套接字、硬件设备等。当然包括操作普通正规文件(Regular
    File)。
  • fopen是用来操纵普通正规文件(Regular File)的。
4. 文件IO层次

如果从文件IO的角度来看,前者属于低级IO函数,后者属于高级IO函数。低级和高级的简单区分标准是:谁离系统内核更近。低级文件IO运行在内核态,高级文件IO运行在用户态。
open 更依赖内核。

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

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

    使用fopen函数,由于在用户态下就有了缓冲,因此进行文件读写操作的时候就减少了用户态和内核态的切换(切换到内核态调用还是需要调用系统调用API:read,write);而使用open函数,在文件读写时则每次都需要进行内核态和用户态的切换;表现为,如果顺序访问文件,fopen系列的函数要比直接调用open系列的函数快;如果随机访问文件则相反。

但是对于现在的电脑cpu 核心和配置内存来说这个效率微乎其微。

标准库函数的使用

标准库包含头文件 <stdio.h>

fopen
FILE *fopen(char *filename, char *mode)
filename:文件名称
mode 打开模式:                                            
    r   只读方式打开一个文本文件                           
    rb  只读方式打开一个二进制文件                         
    w   只写方式打开一个文本文件                           
    wb  只写方式打开一个二进制文件                         
    a   追加方式打开一个文本文件                           
    ab  追加方式打开一个二进制文件                         
    r+  可读可写方式打开一个文本文件                       
    rb+ 可读可写方式打开一个二进制文件                     
    w+  可读可写方式创建一个文本文件                       
    wb+ 可读可写方式生成一个二进制文件                     
    a+  可读可写追加方式打开一个文本文件                   
    ab+ 可读可写方式追加一个二进制文件
fwrite
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
//ptr 缓冲区
//size 写的文件的大小
//nmemb 写的次数
demo:
#include <stdio.h>
#include <string.h>

int main()
{
	FILE *fp;
	char *buf = "gu hen shuai!";
	char readBuf[64] = {'\0'};
	
	fp = fopen("gu.txt","w+");

	fwrite(buf,sizeof(char),strlen(buf),fp);

	fseek(fp,0,SEEK_SET);

	fread(readBuf,sizeof(char),strlen(buf),fp);

	printf("read data :%s\n",readBuf);

	fclose(fp);

	return 0;
}
write ,read和 fwrite,fread 的返回值问题:

write ,read返回值是写了多少返回多少,读了多少返回多少,而fwrite,fread的返回值是与它的第三个参数有关

fgetc,fputc,feof

fputc
int fputc(int c, FILE *stream);
向文件写一个字符或数字
feof
int feof(FILE *stream);
到了文件的尾巴后会变成0,没到为非0

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值