学习C高级(二十一)

系统调用

系统调用
  OS API:操作系统的各个模块提供给上层开发App用的函数
  API:Application Programming Interface 应用程序编程接口
  接口:函数声明+调用函数所需的辅助信息的统称,以头文件形式提供
  OS API ---- 系统调用 ---- 对应函数被调用时,实际是调用一个操作系统提供的功能

UNIX家族的操作系统:
1) System V: AIX HPUX
2) BSD:FreeBSD OpenBSD NetBSD
3) 类UNIX:Linux

分类:
1) 任务控制 ------ OS任务管理模块提供
    进程
    线程
2) 文件系统控制 — OS文件管理模块、OS设备管理模块提供
3) 系统控制 ---- 杂类
4) 内存管理 ----- OS内存管理模块提供
    malloc — C库
    真正动态分配的核心在操作系统里 — brk
5) 网络管理 ---- OS网络管理模块提供
6) 用户管理
7) 进程间通信 ------ OS任务管理模块提供

IO操作

Unix家族OS的共同特征之一 ------- 一切皆文件
Unix家族OS的三大分支:
1) System-V系列 : AIX HPUX
2) BSD系列:FreeBSD OpenBSD NetBSD
3) 类UNIX系列:Linux Minix

文件种类

1) -  普通文件,有文件内容存储在外存的文件
2) d  directory 目录文件 (目录、文件夹)
3) l  link 链接文件
4) p  pipe 管道文件
5) b  block 块设备文件
6) c  char 字符设备文件
7) s  socket 本地socket文件

普通文件、字符设备文件、管道文件:open close read write lseek
目录文件:opendir closedir readdir seekdir telldir mkdir rmdir
socket文件:网络编程统一的接口
Link文件:链接文件 使用那套接口要看其链接对象是哪种文件

文件系统

文件系统定义:如何存放文件三种信息和如何关联文件夹和文件的手段

保存一个文件需要保存哪些信息?
1) 文件内容
2) 文件名
3) 文件属性信息 ---- 元信息

格式化:给分区指定文件系统的过程
  windows:FAT32 NTFS
  Linux:EXT4

操作文件的公共函数

stat 获取文件的元信息

int stat(const char *pathname,struct stat *statbuf)
功能:获取指定文件的元信息
参数:
  pathname:指向空间存放着一个字符串,字符串内容是一个带路径的文件名,如果不带路径,默认为当前目录 值参数
  statbuf:指向空间用来存放指定文件的元信息 结果参数
返回值:正常返回为0,错误返回-1

用static修饰时:
1)局部变量 ---- 改变了生存期
2) 全局变量 ---- 改变了作用域
3) 函数定义 ---- 改变了作用域

//stat结构体类型
struct stat
{
	off_t st_size;//普通文件时为文件大小(bytes)
	mode_t st_mode;//可以知道文件种类
	。。。。//其它成员暂时不需要知道
};
//stat的使用方式
struct stat meta = {0};
stat("????/file1.dat",&meta);

使用stat函数的例子

/*
从main传参,获取指定文件的元信息
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,char *argv[]){	
	int ret = 0;
	struct stat meta;
	if(argc < 2){//argc参数小于2个退出
		printf("argument too few\n");
		return 1;
	}
	ret = stat(argv[1],&meta);//获取文件的元信息
	if(ret < 0){//获取文件元信息失败退出
		printf("call stat failed\n");
		return 2;
	}
	/*
	st_mode & S_IFMT结果为:
	S_IFDIR:指定文件为目录文件
	S_IFREG: 指定文件为普通文件
	S_IFIFO:指定文件为管道文件
	S_IFBLK: 指定文件为块设备文件
	S_IFCHR:指定文件为字符设备文件
	S_IFSOCK:指定文件为socket文件
	S_IFLNK:指定文件为链接文件
	*/
	switch(meta.st_mode & S_IFMT){
		case S_IFDIR:
			printf("%s is directory\n",argv[1]);
			break;
		case S_IFBLK:
			printf("%s is block-device\n",argv[1]);
			break;
		case S_IFCHR:
			printf("%s is char-device\n",argv[1]);
			break;
		case S_IFLNK:
			printf("%s is link\n",argv[1]);
			break;
		case S_IFIFO:
			printf("%s is fifo\n",argv[1]);
			break;
		case S_IFREG:
			printf("%s is regular file\n",argv[1]);
			printf("The size of %s is %ld\n",argv[1],meta.st_size);
			break;
		case S_IFSOCK:
			printf("%s is socket\n",argv[1]);
			break;
		default:
			printf("type is unkonw\n");
			break;
	}
	return 0;
}

在这里插入图片描述

access 检查文件权限

int access(const char *pathname,int mode)
功能:检查对指定文件是否具有指定操作权限
参数:
  pathname:指向空间存放着一个字符串,字符串内容是一个带路径的文件名,如果不带路径,默认为当前目录值参数
  mode:
    R_OK,检查有没有读权限
    W_OK,检查有没有写权限
    X_OK,检查有没有可执行权限
    F_OK,检查有没有文件是否存在 (最常用)
    R_OK W_OK X_OK F_OK中几个的位或

/*R_OK | X_OK :检查是否同时具有读权限和可执行权限
返回值:如果具有相应权限则返回0,否则返回-1*/
int ret = 0;
ret = access("????/filename",F_OK);
if(ret < 0)
{//文件不存在
	printf("Not exist\n");
	//出错处理
}
else
{//文件存在
}

unlink 删除指定文件

int unlink(const char *pathname)
功能:删除指定文件
参数:
  pathname:指向空间存放着一个字符串,字符串内容是一个带路径的文件名,如果不带路径,默认为当前目录 值参数
返回值:删除成功返回0,否则返回-1

mkdir 创建一个空目录

int mkdir(const char *pathname,mode_t mode)
功能:创建一个空目录
参数:
  pathname:指向空间存放着一个字符串,字符串内容是一个带路径的新目录名,如果不带路径,默认为当前目录 值参数
  mode:指定新建目录的使用权限
    
    指定方式1:宏常量位或法(man 2 open)
    S_IRWXU S_IRWXG S_IRWXO
    S_IRUSR S_IRGRP S_IROTH
    S_IWUSR S_IWGRP S_IWOTH
    S_IXUSR S_IXGRP S_IXOTH
    S_IRWXU | S_IRWXG | S_IRWXO:所有用户具有读写可执行权限
    
    指定方式2:三位八进制数法
    0777 – 111 111 111 所有用户具有读写可执行权限
    0543 – 101 100 011 主人可读可执行,小组用户可读,其它用户可写可执行
返回值: 成功返回0,否则返回-1

/*
检查文件是否存在,不存在则创建目录文件,存在则判断是目录文件还是普通文件,
若是普通文件输出文件名及文件大小
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,char *argv[])
{
	int ret = 0;
	struct stat meta;

	if(argc < 2){
		printf("argument too few\n");
		return 1;
	}
	ret = access(argv[1],F_OK);//判断文件是否存在
	if(ret != 0){//文件不存在
		mkdir(argv[1],0777);//创建文件
	}else{//文件存在
		stat(argv[1],&meta);//获取文件的元信息
		if((meta.st_mode & S_IFMT) == S_IFREG){
			printf("The size of %s is %ld\n",argv[1],meta.st_size);
		}else{
			printf("%s is not a regular file\n",argv[1]);
		}
	}
	return 0;
}

在这里插入图片描述

rmdir 删除一个空目录

int rmdir(const char *pathname)
功能:删除一个空目录
参数:
  pathname:指向空间存放着一个字符串,字符串内容是一个带路径的目录名,如果不带路径,默认为当前目录 值参数
返回值:成功返回0,否则返回-1

opendir 打开目录

DIR *opendir(const char *pathname)
功能:打开指定的目录,以便于对其做下一步操作
参数:
  pathname:其指向空间存在着一个字符串 字符串内容为带路径的目录名(无路径则为当前目录下)
返回值:
  成功返回一个有效地址值,此后以此地址值代表已打开的目录
  失败返回NULL

closedir 关闭目录

int closedir(DIR *dirp)
功能:关闭已打开的目录,此后该地址值不再代表opendir关联的目录
参数:
  dirp:代表已打开目录的DIR类型的地址值
返回值:
  成功0,失败-1

readdir 读子目录内容

struct dirent *readdir(DIR *dirp)
功能:读目录指示器当前指示的子内容,并且将指示器指示到下一个内容
参数:
  dirp:代表已打开目录的DIR类型的地址值
返回值:
  成功返回static局部变量(变量类型为struct dirent)的地址,该变量中存放读出的子内容的信息
  失败返回NULL

备注:后一次调用会将前一次的结果覆盖

struct dirent
{
	char d_name[256];//存放着子内容的名字
	unsigned char d_type;//存放着子内容的种类
	         //DT_BLK DT_CHR DT_DIR DT_REG DT_LNK 
	         //DT_FIFO DT_SOCK DT_UNKNOWN
	//其它成员
};	

使用readdir函数读取目录内的内容的例子

/*
判断一个带路径文件内的内容,如果是.h或.c文件还要输出文件大小
*/
/*1. 编码时不知具体大小,只有执行到某处通过计算才能知道具体大小,此时应该动态分配
2. 编码时知道具体大小,但所需空间太大,仍然要用动态分配*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int IsEndCorH(const char *str);//函数功能:判断文件是否是.h或.c文件
int main(int argc,char *argv[]){
	DIR *pd = NULL;//创建一个文件指针
	struct dirent *pContent= NULL;//为了获取文件夹内容,所使用的结构体指针
	struct stat meta;//该变量中存放元信息
	char *pathname = NULL;
	int len = 0;

	if(argc < 2){//参数少于2个退出
		printf("argument too few\n");
		return 1;
	}
	/*打开目录*/
	pd = opendir(argv[1]);
	if(NULL == pd){
		printf("opendir failed\n");
		return 2;
	}
	pContent = readdir(pd);//读目录下的内容
	while(pContent != NULL){//成功读取到一个文件
		if(pContent->d_type == DT_REG && IsEndCorH(pContent->d_name)){
			//计算带路径的普通文件名所需空间大小
			len = strlen(argv[1]) + strlen(pContent->d_name) + 2;//加2的原因是字符串以'\0'结尾①,下面要用的组合路径'/'也占位置②
			pathname = (char *)malloc(len);
			if(NULL == pathname){
				printf("Malloc failed\n");	
			}else{
				/*组合带路径的普通文件名*/
				memset(pathname,0,len);
				strcpy(pathname,argv[1]);
				strcat(pathname,"/");//加2的原因在这①
				strcat(pathname,pContent->d_name);//加2的原因也在这②
				stat(pathname,&meta);//获取元信息
				printf("The size of %s is %ld\n",pContent->d_name,meta.st_size);
				free(pathname);
				pathname = NULL;
			}
		}
		pContent = readdir(pd);//读目录里的下一个内容
	}
	closedir(pd);
	pd = NULL;	
	return 0;
}

int IsEndCorH(const char *str){
	int len = strlen(str);
	const char *p = NULL;
	if(len < 2){
		return 0;
	}
	p = str + strlen(str) - 2;//减2的原因是指针回退要找到.c和.h文件
	if(strcmp(p,".c") == 0 || strcmp(p,".h") == 0){
		return 1;
	}else{
		return 0;
	}
}

在这里插入图片描述

/*
读取一个目录下的所有内容,包括子目录下的内容(递归调用)
*/
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int DisplayDir(const char *dirname);
int main(int argc,char *argv[]){
	if(argc < 2){//参数少于2个退出
		printf("argument too few\n");
		return 1;
	}
	DisplayDir(argv[1]);
	return 0;
}

int DisplayDir(const char *dirname){
	DIR *pd = NULL;
	struct dirent *pt = NULL;
	char *path = NULL;
	int len = 0;

	pd = opendir(dirname);
	if(NULL == pd){//打开目录失败
		return -1;
	}
	pt = readdir(pd);//读取一个目录内容
	while(pt != NULL){
		if(pt->d_type == DT_DIR && 
			strcmp(pt->d_name,".") != 0 && 
			strcmp(pt->d_name,"..") != 0)
			{//该内容为目录    
			len = strlen(dirname) + strlen(pt->d_name) + 2;//分配空间		
			path = (char *)malloc(len);
            //组合带路径的新目录名
			if(path != NULL){
				memset(path,0,len);
				strcpy(path,dirname);
				strcat(path,"/");
				strcat(path,pt->d_name);
				DisplayDir(path);//继续取目录内的内容,(递归调用)
				free(path);//释放存放新目录名的空间
				path = NULL;
			}else{
				printf("Malloc Failed\n");
			}
		}else{//该内容不是目录
			printf("%s/%s\n",dirname,pt->d_name);//显示路径和文件名
		}
		pt = readdir(pd);
	}
	closedir(pd);
	return 0;
}
/*
判断给定的文件是否存在,如果不存在创建一个空目录,
如果存在是普通文件则显示文件名和文件大小,
如果存在是目录文件则显示该目录下所有子内容的种类和名字(不要求显示子目录的内容)
*/
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>

int DisplayDir(const char *dirname);
int main(int argc,char *argv[])
{
	int ret  = 0;
	struct stat meta;

	if(argc < 2){
		printf("argument too few\n");
		return 1;
	}
	ret = access(argv[1],F_OK);//查看文件是否存在
	if(ret < 0){//文件不存在,则创建新文件
		mkdir(argv[1],0777);
	}else{
		stat(argv[1],&meta);
		switch(meta.st_mode & S_IFMT){
			case S_IFREG:
				printf("The size of %s is %ld\n",argv[1],meta.st_size);
				break;
			case S_IFDIR:
				DisplayDir(argv[1]);
				break;
			default:
				printf("This is other type file\n");
				break;
		}		
	}
	return 0;
}

int DisplayDir(const char *dirname)
{
	DIR *pd = NULL;
	struct dirent *pt = NULL;
	pd = opendir(dirname);//打开目录文件
	if(NULL == pd){
		perror("opendir failed");
		return -1;
	}
	pt = readdir(pd);//读取目录的第一个内容
	while(pt != NULL){
		switch(pt->d_type){
			case DT_REG:
				printf("The %s file is regular file\n",pt->d_name);
				break;
			case DT_DIR:
				printf("The %s file is directory file\n",pt->d_name);
				break;
			case DT_SOCK:
				printf("The %s file is socket file\n",pt->d_name);
				break;
			case DT_FIFO:
				printf("The %s file is fifo file\n",pt->d_name);
				break;
			case DT_CHR:
				printf("The %s file is char-device file\n",pt->d_name);
				break;
			case DT_BLK:
				printf("The %s file is block-device file\n",pt->d_name);
				break;
			case DT_LNK:
				printf("The %s file is link file\n",pt->d_name);
				break;
			default:
				printf("The %s file is unknow type file\n",pt->d_name);
				break;
		}
		pt = readdir(pd);//读取目录的下一个内容
	}
	return 0;
}

原生IO操作函数

原生的IO操作函数,其它高级语言的IO操作都是直接或间接调用本套函数实现的,例如:C库中f开头的函数。
  本套接口主要侧重于管道文件和字符设备文件,虽然也可以操作普通文件,
但相比于C库中f开头的文件操作函数而言非常不方便。
  操作普通文件都是使用C库函数,特殊文件采用原生系统调用
  
文件的位置指示器:

  1. 普通文件肯定有位置指示器
  2. 管道文件是没有位置指示器
  3. 字符设备文件而言,有的有位置指示器,有的没有位置指示器
  4. 读写函数在有位置指示器时,有两个功能:
      1) 读写数据  
      2)更改指示器的指示位置
  5. 读写函数在没有位置指示器时,只有相应的读写功能

open

int open(const char *pathname,int flags)
int open(const char *pathname,int flags,mode_t mode)//当需要创建新文件时调用
功能:按照指定的模式打开指定的文件,以便于后续操作
参数:
  pathname:指向空间存在着一个字符串,字符串内容为带路径文件名(无路径则为当前目录)
  mode:新建文件时,指定新文件操作权限,同mkdir函数的mode参数
  flags:打开文件的模式标记,O_RDONLY O_WRONLY O_RDWR:三者选其一,然后与下列标记选择性进行位或:
    O_CREAT: 当有O_WRONLY或者O_RDWR标记,而文件不存在时,有此标记则表示需要创建新文件,只要有此标记则应调用三参数open函数
    O_APPEND:有此标记,写操作总是在文件尾追加新内容
    O_TRUNC:当支持写操作时,有此标记表示需要清空文件原内容
    O_NONBLOCK:有此标记表示对该文件的读写操作为非阻塞,对普通文件的读写操作总是非阻塞的,因此该标记主要针对管道文件和字符设备而言
返回值:
  成功返回一个大于等于0的整数,而该整数在此后代码中用代表被操作的文件,这个整数被称为描述符(文件描述符),描述符的取值范围>=0
  失败返回-1

几个特殊的描述符:
  0 标准输入设备 ----- stdin 是C库中普通文件操作函数帮我们定义好的FILE *类型的全局变量
  1 标准输出设备 ----- stdout
  2 标准错误设备 ----- stderr
说明:
  1. 这三个特殊文件,是系统默认为每个程序main函数调用前就已open的,在程序中可以直接用这三个数字代表的特殊设备文件
  2. 从标准输入读一个字符,可以用两种手段
    char ch = ‘\0’;
    ch = getchar();
    read(0,&ch,1);
  3. 向标准输出显示一个字符,可以用两种手段
    putchar(‘k’);
    char ch = ‘k’;
    write(1,&ch,1);

int fd = -1;
fd = open(....);
if(fd < 0)
{
	//出错处理
}	

fopen(char *filename.char *mode)
  “r” ----> O_RDONLY
  “w” ----> O_WRONLY | O_CREAT | O_TRUNC
  “a” ----> O_WRONLY | O_CREAT | O_APPEND
  “r+” ----> O_RDWR
  “w+” ----> O_RDWR | O_CREAT
  “a+” ----> O_RDWR | O_CREAT | O_APPEND

close

int close(int fd)
功能:关闭已打开的文件,此后fd中描述符不再代表已打开的文件
参数:
  fd:代表已打开文件的描述符
返回值:
  成功为0,失败-1

//关闭文件后要把指针重新赋值-1 
  close(fd);
  fd = -1;

read

ssize_t read(int fd,void *buf,size_t count)
功能:从已打开文件中读取数据到指定的内存空间中
参数:
  fd:代表已打开的文件的描述符
  buf:用来存放数据的内存空间的首地址
  count:本次调用期望读的数据总字节数
返回值:
  成功返回本次调用实际读到的字节数
    0: 到文件尾(普通文件而言)
  错误-1

备注:
  如果打开文件时,没有O_NONBLOCK标记,该函数读不到数据时会阻塞(普通文件无效),即等待有新数据产生
  如果打开文件时,有O_NONBLOCK标记,该函数读不到数据时非阻塞,即立即返回错误

write

ssize_t write(int fd,const void *buf,size_t count)
功能:将指定内存空间中的数据写入到已打开文件中
参数:
  fd:代表已打开的文件的描述符
  buf:被写数据所在的内存空间的首地址
  count:本次调用期望写的数据总字节数
返回值:
  成功返回本次调用实际写成功的字节数
  错误-1

备注:
  如果打开文件时,没有O_NONBLOCK标记,该函数当设备缓冲区满时会阻塞(普通文件无效),即等待缓冲区有空余空间可以继续写
  如果打开文件时,有O_NONBLOCK标记,该函数当设备缓冲区满时非阻塞,即立即返回错误

/*
实现mycat,完成将一个普通文件内容显示到命令行
要求读文件内容、文件内容输出到命令行全部用系统调用实现
文件名通过main函数参数传入
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,char *argv[])
{
	int fd = -1;
	char ch = '\0';
	int ret = 0;

	if(argc < 2){//参数少于2退出
		printf("argument too few\n");
		return 1;
	}
	fd = open(argv[1],O_RDONLY);//自读形式打开文件
	if(fd < 0){//打开文件失败退出
		printf("open %s failed\n",argv[1]);
		return 2;
	}
	ret = read(fd,&ch,1);//读取文件一个字节内容到ch
	while(ret == 1){
		write(1,&ch,1);//1 标准输出设备stdout,把ch里的信息写到标准输出设备
		ret = read(fd,&ch,1);//读取下一个字节
	}
	close(fd);//关闭文件
	fd = -1;
	return 0;
}
/*
实现mycp,完成将一个普通文件内容拷贝到另一个文件
文件名通过main函数参数传入
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc,char *argv[])
{
	int fdr = -1;
	int fdw = -1;
	char ch = '\0';
	int ret = 0;

	if(argc < 3){//参数少于3个退出
		printf("argument too few\n");
		exit(1);
	}
	fdr = open(argv[1],O_RDONLY);//以只读形式打开文件
	if(fdr < 0){//文件打开失败退出
		printf("open %s failed\n",argv[1]);
		return 2;
	}	
	fdw = open(argv[2],O_WRONLY | O_CREAT | O_TRUNC,0666);//以只写形式打开文件,文件不存在时创建文件
	if(fdw < 0){//打开文件失败退出
		printf("open %s failed\n",argv[2]);
		return 3;
	}	
	ret = read(fdr,&ch,1);//从文件读取一个字节内容到ch
	while(ret == 1){
		ret =  write(fdw,&ch,1);//把ch里的信息写入到fdw描述符所指的文件中
		if(ret != 1) {//文件写入失败就不再输出
			printf("write data error\n");
			break;
		}
		ret = read(fdr,&ch,1);//从文件读下一个字节
	}
	close(fdr);//关闭文件
	fdr = -1;
	close(fdw);
	fdw = -1;
	return 0;
}

lseek

off_t lseek(int fd,off_t offset,int whence)
功能:对于有位置指示器文件,该函数用来改变位置指示器的指示位置
参数:
  fd:代表已打开的文件的描述符
  whence:
    SEEK_SET  将读写位置指向文件头后再增加offset个位移量。
    SEEK_END 以目前的读写位置往后增加offset个位移量。
    SEEK_CUR 将读写位置指向文件尾后再增加offset个位移量。
  offset:相对于指定文件的偏移量
返回值:
  成功返回指示器新的指示位置
  失败-1

int loc = 0;
loc = lseek(fd,0,SEEK_CUR); //与ftell功能相同
/*
有一个Person结构体含如下成员:人名、性别、职业,定义并初始化
一个含5个该结构体类型元素的数组,
将这些元素保存到文件person.dat中
(前4字节存放男士数目,紧接4个字节存放女士数目,后续存放每个Person的数据)
然后再从文件中读出所有女性的信息
要求所有文件读写用原生系统调用函数完成
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Person
{
	char name[20];
	char sex;
	char worker[32];
};

#define N 5
struct Person *ReadAllFemale(int fd,int *numout);
int main()
{
	struct Person arr[N] = {{"XiaoCai",'F',"Maiguoze"},
							{"Laote",'M',"Dazuiba"},
							{"XiaoFang",'F',"Teacher"},
							{"LaoLiu",'M',"Manager"},
							{"Bingbing",'F',"Mingxing"}
                           };
	int malenum = 0;//男人数
	int femalenum = 0;//女人数
	int i = 0;
	int fd = -1;//打开文件的返回值
	int ret = 0;//write函数的返回值
	struct Person *pst = NULL;

	for(i = 0;i < N;i++)//统计男女人数
	{
		if(arr[i].sex == 'F')//统计女人数
		{
			femalenum++;
		}
		else if(arr[i].sex == 'M')//统计男人数
		{
			malenum++;
		}
	}

	do
	{
		fd = open("person.dat",O_RDWR | O_CREAT | O_TRUNC,0666);//以写的方式打开文件
		if(fd < 0)//打开文件失败
		{
			perror("open failed");
			break;
		}

		ret = write(fd,&malenum,sizeof(int));//写入男人数
		if(ret != sizeof(int))//写入文件的字节数不符合预期要写入的字节数,即写入文件失败
		{
			perror("write malenum error");
			break;
		}

		ret = write(fd,&femalenum,sizeof(int));//写入女人数
		if(ret != sizeof(int))
		{
			perror("write femalenum error");
			break;
		}

		ret = write(fd,arr,sizeof(arr));//写入所有person信息	
		if(ret != sizeof(arr))
		{
			perror("write person data error");
			break;
		}

		pst = ReadAllFemale(fd,&femalenum);//读取所有女人信息
		if(NULL == pst)//读取失败后的处理
		{
			printf("read female num failed\n");
			break;
		}

		for(i = 0;i < femalenum;i++)//读取成功后的处理
		{
			printf("Name:%s,Sex:%c,Work:%s\n",(pst+i)->name,(pst+i)->sex,(pst+i)->worker);
		}
	}while(0);//这个目的是为了好处理,简化if---else

	if(fd >= 0)//关闭文件,因为打开文件失败时-1不用关闭文件
	{
		close(fd);
		fd = -1;
	}
	if(pst != NULL)//因为申请空间失败不用释放空间
	{
		free(pst);
		pst = NULL;
	}
	return 0;
}

struct Person *ReadAllFemale(int fd,int *numout)
{
	int num = 0;
	int ret = 0;
	struct Person *pst = NULL;
	struct Person data = {""};
	int i = 0;

	lseek(fd,sizeof(int),SEEK_SET);//让指示器指示到4号字节,SEEK_SET表示从文件开始的位置
	ret = read(fd,&num,sizeof(int));//读取女人数,之后位置指示器指示到8号字节
	if(ret != sizeof(int))
	{
		perror("read famalenum error");
		return NULL;
	}
	if(num <= 0)
	{
		printf("No female data\n");
		return NULL;
	}
	*numout = num;//女人数
    //申请大小空间为女人数*person大小的空间
	pst = (struct Person *)malloc(num * sizeof(struct Person));
	if(NULL == pst)//申请空间失败后的处理
	{
		perror("Malloc Failed");
		return NULL;
	}
	memset(pst,0,num * sizeof(struct Person));//申请空间成功后的处理

	ret = read(fd,&data,sizeof(data));//读取一个人的信息
	while(ret == sizeof(data))//读取的信息没有丢失或增多,即读取成功
	{
		if(data.sex == 'F')//女
		{
			memcpy(pst+i,&data,sizeof(data));
			i++;
		}
		ret = read(fd,&data,sizeof(data));//读取下一个人的信息
	}

	return pst;//返回申请空间的首地址
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值