Linux C文件操作

文章目录

linux下的文件操作包括两种,一种是使用C函数,一种是使用系统调用。

  • gcc 常用来实现c程序的编译
  • gcc filename.c 编译,链接(自动)后输出可执行文件a.out
  • 只是输入./a.out就可以执行filename程序
  • gcc -o filename filename.o 可以生成一个filename的可执行文件,直接执行filename就可以直接执行代码中的内容(与上一条相比,只是执行输出的名称)
  • gcc -S filename.c 可以生成一个filaname.s的汇编文件,汇编文件与底层执行时一一对应的,可以用来调试
  • gcc -O 可以用来编译优化,但是不是所有情况下都可以做编译优化。
  • 还可以使用gcc -c filename.cgcc -o filename filename.o分步编译、链接

文件操作函数

文件操作函数包括fopen、fgetc、fputc函数等,这是标准的C函数

c 复制代码
// 把file.in中给的内容输出给file.out
#include<stdio.h>
#include<stdlib.h>

int main(){
	int c;
	FILE *in,*out;//定义两个文件指针类型
	in=fopen("file.in","r");//以只读的方式打开file.in文件,成功后返回文件指针in
	out=fopen("file.out","w");//以只写的方式打开file.out文件,成功后返回文件out
	while((c=fgetc(in))!=EOF)//从in文件中读,把读出来的数据写到out中
		fputc(c,out);
	fclose(in);
	fclose(out);
	exit(0);
}
  • FILE * fopen(char *path,char *mode),第一个参数表示路径,第二个参数表示模式,fopen的返回值是一个文件指针,如果文件打开失败则返回一个NULL,可以使用fclose()函数关闭文件:

    • 模式包括<r,w,a>
    • r表示以只读的方式打开,文件指针指向了文件的起始位置,r表示的文件必须存在。
    • w表示以写入的方式打开,如果文件不存在则创建文件,如果文件存在则先清空文件。
    • a表示以追加模式打开,对一个文件的写入,如果文件不存在就创建文件,如果文件存在就在文件末尾追加内容。
    • 如果后面有+表示可读写;
    • 如果后面有b表示打开的是二进制文件
    • 如果后面有t表示打开的是文本文件。
  • char *fgets(char *s,int size,FILE *stream)表示从指定的文件中读下一个字符,返回一个没有符号的字符,或者EOF(end of file),需要读的文件必须是读或者读写的方式打开的,并且文件存在。

  • fputc(char c,FILE * fp)把内容写入到指定文件。

  • 可以使用diff file.in file.out比较两个文件的内容,没有输出表示两个文件一模一样。

  • fclose,最重要的是把文件从内存中写回磁盘。

为了程序的健壮性,可以把以上的文件改为以下,防止读的文件没有权限或者不存在

c 复制代码
// 把file.in中给的内容输出给file.out
#include<stdio.h>
#include<stdlib.h>

int main(){
	int c;
	FILE *in,*out;//定义两个文件指针类型
	if((in=fopen("file.in","r"))==NULL){//以只读的方式打开file.in文件,成功后返回文件指针in
		print("file.in文件不存在!")
		return 0;
	}
	out=fopen("file.out","w");//以只写的方式打开file.out文件,成功后返回文件out
	while((c=fgetc(in))!=EOF)//从in文件中读,把读出来的数据写到out中
		fputc(c,out);
	
	exit(0);
}

文件系统调用

文件描述符以及open、read、write

c 复制代码
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<stdlib.h>

int main(){
	char block[1024];
	int in,out;
	int nread;
	
	if((in=open("file.in",O_RDONLY))==-1){// 3
		print("文件打开错误!");
		return 0;}
	out=open("file.out",O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);// 4
	while((nread=read(in,block,sizeof(block)))>0)
		write(out,block,nread);

	return 0;
}
  • int open(constchar*pathname,int flags,mode_t mode);如果成功则返回一个文件描述符(integer,小的非负整数且没有被使用过),否则返回-1;对于open函数来说,第三个参数当且仅当创建新文件(使用O_RDONLY)才是用,用于指定文件的访问权限,比如权限为777;pathname表示要打开或者创建的文件的路径名;flags表示指定文件的打开模式,flags的值包括以下,打开或者创建文件时,至少要使用一个以下命令:

    • O_RDONLY:只读模式
    • O_WRONLY:只写模式
    • O_RDWR:读写模式
    • O_CREAT:文件不存在时创建文件
  • mode的值包括以下

    • S_IRUSR:允许文件的所有者阅读它
    • S_IWUSR:允许文件所有者写它
    • S_IRGRP:允许文件组读取它
    • S_IWGRP: 允许文件组编写它

linux下任何一个进程都有3个默认打开的文件描述符:0表示标准输入(键盘),1表示标准输出(显示器),2表示标准错误输出(显示器)

  • ssize_t read(int fd, void *buf, size_t count);表示读取指定字节数的数据到缓冲区buff中,同时文件的当前位置向后移动;读取成功返回读取的字节数,出错返回-1并且设置errno,如果在调用read之前已经到达文件末尾则返回0(例如,距文件末尾还有30个字节而请求读100个字节,则read返回30,下次read将返回0);

  • ssize_t write(int fd, const void *buf, size_t count); 成功返回写入的字节数,出错返回-1并设置errno写常规文件时

系统调用与标准函数c的调用的区别

使用time 文件路径,可以知道文件运行所需要的时间

使用scrace 文件路径,可以跟踪文件

  • 使用fgetc或者fputc的时候,它底层的调用还是调用了read和write,但是在读取的时候做了一个系统的优化,也就是一次性读取了多个字节的内容,表面上看起来只读取一个字符,实际上底层中读取的是多个字节的字符。

  • 注意系统调用这个读写位置和使用C标准I/O库时的读写位置有可能不同,这个读写位置是记在内核中的,而使用C标准I/O库时的读写位置是用户空间I/O缓冲区中的位置。比如用fgetc读一个字节,fgetc有可能从内核中预读1024个字节到I/O缓冲区中,再返回第一个字节,这时该文件在内核中记录的读写位置是1024,而在FILE结构体中记录的读写位置是1

文件的读取位置

标准c函数

标准的c函数中,不可以对FILE的文件指针直接进行++,--,可以使用fseek函数来控制文件的读写指针。

  • int fseek(FILE *stream, long offset, int fromwhere);重定位流上的文件指针,函数设置文件指针stream的位置。如果执行成功,stream将指向以fromwhere为基准,偏移offset个字节的位置。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置;返回值: 成功,返回0,否则返回其他值;第一个参数stream为文件指针;第二个参数offset为偏移量,整数表示正向偏移,负数表示负向偏移;第三个参数origin设定从文件的哪里开始偏移,可能取值为:

    • SEEK_SET: 文件开头,用0表示
    • SEEK_CUR: 当前位置,用1表示
    • SEEK_END: 文件结尾,用2表示
  • fseek(fp,100L,0);把fp指针移动到离文件开头100字节处;

    fseek(fp,100L,1);把fp指针移动到离文件当前位置100字节处;

    ffseek(fp,-100L,2);把fp指针退回到离文件结尾100字节处。

系统调用

系统调用使用lseek来调用文件的文件位置,与c函数调用相比,c文件调用使用的是文件指针,而系统调用使用的是文件描述符号

  • off_t lseek(int fd, off_t offset, int whence);fd表示函数描述符,参数含义与上面的c函数调用一致。

空洞文件

可以使用以上的fseek和lseek去创建一个空洞文件

① 空洞文件就是这个文件有一段是空的;

② 普通文件中间不能有空,write文件时从前往后移动文件指针,依次写入;

③ 用lseek往后跳过一段,就形成空洞文件;

④ 空洞文件对多线程共同操作文件非常有用。需要创建一个很大文件时,从头开始依次创建时间很长,可以将文件分成多段,多个线程操作每个线程负责其中一段的写入。

文件的内存映射操作

  • mmap是一种内存映射文件的方法,即将一个文件或者其他的映射对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟控件中一段虚拟地址的映射关系。实现这种映射关系之后,进程可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,也就是对文件的操作不必调用read或者write等系统调用函数,相同的,在内核空间内这一段的区域也直接改为用户空间,从而实现不同进程的文件的共享。
  • 使用mmap进行映射,会得到一个磁盘和某一个文件的地址相同的地址,当往这个地址写内容的时候,内容会直接写到文件中,比正常的系统调用要少一次拷贝的过程。
  • 正常调用写文件的流程图

  • mmp内存映射写文件的流程图

  • void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)第一个参数表示映射的地址,如果地址设置为NULL,内核会自动挑一个地址来进行映射;第二个参数表示映射有多长;第三个参数描述了要求的内存保护,包括PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE;最后一个参数表明如果对内存进行修改能否被其他的进程看到,包括MAP_SHARED和MAP_PRIVATE。

c 复制代码
#include<stdio.h>
#include<unistd.h>
#include<sys/mman.h>
#include<fcntl.h>
#include<stdlib.h>

typedef strucy{
	int integer;
	char string[24];
}RECORD;

#define NRECORDS(100)

int main(){
	RECORD record,*mapped;
	int i,f;
	FILE *fp;
	
	fp=fopen("records.dat","w+");
	for(i=0;i<NRECORDS;i++){
		record.integer=i;
		sprintf(record.string,"RECORD-%d",i);
		fwrite(&record,sizeof(record),1,fp);//写record的数据,长度为record的大小,写一次,写到fp文件中
	}
	fclose(fp);
	
	fp=fopen("records.dat","r+");
	fseek(fp,43*sizeof(record),SEEK_SET);
	fread(&record,sizeof(record),1,fp);
	//修改fp的数据
	record.integer=143;
	sprintf(record.string,"RECORD-%d",record.integer);
	
	//修改后写回
	fseek(fp,-1*sizeof(record),SEEK_CUR);
	fwrite(&record,sizeof(record),1,fp);
	fclose(fp);

	//----------------------------------------------------------
	//使用mmap
	f=open("records.dat",O_RDWR);
	mapped=(RECORD *)mmap(0,NRECORDS*sizeof(record),PROT_RDAD|PROT_WRITE,MAP_SHARED,f,0);//第一个参数0表示内核挑选映射的地址;第二个参数表示映射的大小;第三个参数表示权限,可读可写;第四个参数表示可以共享;第五个参数表示被映射的文件;第六个参数表示映射从文件起始的位置开始的偏移量。执行完成后,整个文件全部映射到内存中,相当于一个数组mapped
	mapped[43],integer=243;
	sprintf(mapped[43].string,"RECORD-%d",mapped[43].integer);//操作数组一样操作内存
	msync((void*)mapped,NRECORDS*sizeof(record),MS_ASYNC);//把内存和辅存中的文件同步起来
	munmap((void*)mapped,NRECORDS*sizeof(record));//解开映射
	close(f);
	
	return 0;
}

文件目录

文件目录对应的是文件的属性信息,FCB文件控制块,存放文件的属性信息;文件的属性信息与文件的实体不是存在在一个位置上的,把文件的属性信息存放在一个位置,这一类的文件叫做目录文件,也就是windows上的文件夹

c 复制代码
#include<unistd.h>
#include<stdio.h>
#include<dirent.h>
#include<string.h>
#include<sys/stat.h>
#include<stdlib.h>

void printdir(char *dir,int depth){
	DIR *dp;
	struct dirent *entry;
	struct stat statbuf;
	if((dp=opendir(dir))==NULL){
		fprintf(stderr,"cannot open directory:%s\n",dir);
		return;
	}
	chdir(dir);
	while((entry=readdir(dp))!=NULL){
		lstat(entry->d_name,&statbuf);
		if(S_ISDIR(statbuf.st_mode)){
			if(strcmp(".",entry->d_name)==0||strcmp("..",entry->d_name)==0)
				continue;
		printf("%*s%s/\n",depth,"",entry->d_name);
		printdir(entry->d_name,depth+4);//递归调用,深度优先搜索
		}
		else printf("%*s%s\n",depth,"",entry->d_name);
	}
	chdir("..");
	closedir(dp);
}

int main(){
	printf("Directory scan of /home:\n");
	printdir("/home",0);
	printf("done\n");
	return 0;
}
相关推荐
码农君莫笑1 分钟前
Blazor项目中使用EF读写 SQLite 数据库
linux·数据库·sqlite·c#·.netcore·人机交互·visual studio
mubeibeinv12 分钟前
项目搭建+图片(添加+图片)
java·服务器·前端
无为之士22 分钟前
Linux自动备份Mysql数据库
linux·数据库·mysql
秋名山小桃子31 分钟前
Kunlun 2280服务器(ARM)Raid卡磁盘盘符漂移问题解决
运维·服务器
与君共勉1213832 分钟前
Nginx 负载均衡的实现
运维·服务器·nginx·负载均衡
荒古前35 分钟前
龟兔赛跑 PTA
c语言·算法
岑梓铭38 分钟前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
努力学习的小廉39 分钟前
深入了解Linux —— make和makefile自动化构建工具
linux·服务器·自动化
MZWeiei42 分钟前
Zookeeper基本命令解析
大数据·linux·运维·服务器·zookeeper
7yewh1 小时前
嵌入式Linux QT+OpenCV基于人脸识别的考勤系统 项目
linux·开发语言·arm开发·驱动开发·qt·opencv·嵌入式linux