学习C(十七)
文件IO
一般用到文件操作时,主函数都加上参数(int argc,char** argv)
到目前位置,我们将所有数据存储在内存中。内存中的数据虽然读取速度非常快,但是一旦程序结束或者意外中断,内存中的数据全都丢失。
为了解决这个问题,我们通常就会在程序结束之前将内存中的数据写入到文件中去,这个操作就叫文件IO(in and out)
第零组:sprintf/sscanf(字符串间的读取)
sprintf,指定内存数据按格式输出到字符串中
#include<stdio.h>
//sprintf可以在内存和内存之间复制数据
int main(){
char buf[512] = "123 456";
char temp[512] = {0};
sprintf(temp,"%s",buf);
//将指针常量buf所指的一段栈内存数据,按照%s的格式复制到指针常量temp所指的一段栈内存空间中,包括空格
printf("%s\n",temp);//输出:123 456
return 0;
}
sscanf,从字符串中数据按格式读取到指定内存
#include<stdio.h>
//ssanf可以在内存和内存之间复制数据
//sscanf():从一个字符串中读进与指定格式相符的数据.
int main(){
char buf[512] = "123 456";
char temp[512] = {0};
int a = 0;
char b = 0;
//将指针常量buf所指的一段栈内存数据,按照%s的格式复制到指针常量temp所指的一段栈内存空间中。注意:遇到空格停止复制。
sscanf(buf,"%s",temp);
sscanf(buf,"%d",&a);
sscanf(buf,"%c",&b);
printf("%s-%d-%c\n",temp,a,b);//输出:123-123-1
return 0;
}
第一组:fprintf/fscanf(全读取)
fprintf和fscanf的函数声明的原型是一样的,功能不一样
函数名:fopen
函数参数:const char* path,char* mod
返回值:返回一个指向了文件path 文件描述符
解释: path:具体路径下的文件名。
mod:该文件的打开方式:
r:以只读方式打开文件,如果文件不存在,则打开失败。打开文件后,光标处于文件的开头位置
r+:以读写的方式打开文件,如果文件不存在,则打开失败。打开文件后,光标处于文件的开头位置
w:以只写的方式打开文件,如果文件不存在,则创建文件后打开,打开文件后,光标处于文件的开头位置
w+:以读写的方式打开文件,如果文件不存在,则创建文件后打开,打开文件后,光标处于文件的开头位置
a:以添加的方式打开文件,如果文件不存在,则创建文件后打开,打开文件后,光标处于文件的末尾位置
a+:以添加和读取的方式打开文件,如果文件不存在,则创建文件后打开,打开文件后,光标处于文件的末尾位置
b(bin):以二进制的形式打开文件
只要该文件在一次fopen和fclose之内,他的光标位置总是向后走的
fprintf,将指定内存数据输出到文件中
函数名:fprintf
函数参数:FILE* stream, const char* format, [argument]
解释:FILE* fp 为一个文件指针,他能完全代表被他指向的文件,我们把这样的一个文件指针称为文件描述符
函数功能:将 str中所有的内容写入到fp所代表的文件中去
函数返回值:返回值是写入文件的字符个数,失败返回-1
那么如何确定fp的指向
使用函数fopen来确定fp的指向
#include<stdio.h>
//把内存中的数据输出到指定文件中
int main(int argc,char** argv){
char buf[20] = "你好,中国";
//FILE* 为声明定义一个文件描述符
//函数fopen返回值为文件描述符
FILE* fp = fopen("./test.txt","w");//w为只写的方式打开文件,打开文件后光标处于文件的开头位置
fprintf(fp,"%s\n",buf);//把内存中的数据输出到文件中
fclose(fp);
return 0;
}
fscanf,从文件中读取数据到指定内存
函数名:fscan
函数参数:FILE * stream, const char * format, [argument…]
函数功能:根据数据格式(format),从输入流(stream)中读入数据,存储到argument中,遇到空格和换行时结束。
函数返回值:整型,成功返回读入的参数的个数,失败返回EOF(-1)。
#include<stdio.h>
//从文件中读取数据到指定内存中
int main(int argc,char** argv){
char buf[20] = "你好,中国";
//FILE* 为声明定义一个文件描述符
//函数fopen返回值为文件描述符
FILE* fp = fopen("./test2.txt","r");//r为只读的方式打开文件,打开文件后光标处于文件的开头位置
int retval = 0;
while(1){
retval = fscanf(fp,"%s",buf);//从文件中读取数据到指定内存中,遇到空格和换行时结束
//此时文件中的光标停在空格或换行符的后面
if(retval==-1){break;}//当文件读取完或出差错时返回-1
printf("%s\n",buf);
}
fclose(fp);
return 0;
}
使用fprintf和fscanf完成学生数组的加载保存
/*
编写一个加载保存学生信息的程序:
要求:先手动完善3个学生的信息,然后将3个学生添加入数组中。
将整个数组保存到文件中去
从文件中读取所有信息进入整个数组
要求:当第一次运行完成个程序后,第二次再次运行程序,同名学生添加失败
学生信息有:姓名,年龄,成绩,要求姓名唯一
*/
#include<stdio.h>
#include<string.h>
typedef struct Stu{
char name[20];
int age;
int score;
}stu_t;
//函数参数:stu_t* DB,int* len
//函数功能:添加学生信息到数组中
void insertStu(stu_t* DB,int* len){
char name[20] = {0};
int age = 0;
int score = 0;
printf("请输入学生的姓名,年龄及分数:\n");
scanf("%s %d %d",name,&age,&score);
while(getchar()!=10);
stu_t st = {0};
strcpy(st.name,name);
st.age = age;
st.score = score;
DB[*len] = st;//结构体之间整体赋值
(*len)++;
}
//函数参数:stu_t* DB,int len
//函数功能:将整个学生数组保存到文件中
void saveStu(stu_t* DB,int len){
FILE* fp = fopen("./stuDB.txt","w");
for(int i=0;i<len;i++){
fprintf(fp,"%s %d %d\n",DB[i].name,DB[i].age,DB[i].score);
}
fclose(fp);
}
//从文件中读取所有信息进入整个数组
void loadStu(stu_t* DB,int* len){
FILE* fp = fopen("./stuDB.txt","r");
if(fp == NULL){return;}
int retval = 0;
while(1){
retval = fscanf(fp,"%s %d %d",DB[*len].name,&DB[*len].age,&DB[*len].score);
if(retval == -1){break;}
(*len)++;
}
fclose(fp);
}
//打印学生信息
void printStu(stu_t* DB,int len){
int i = 0;
for(i=0;i<len;i++){
printf("***********\n");
printf("姓名:%s\n",DB[i].name);
printf("年龄:%d\n",DB[i].age);
printf("成绩:%d\n",DB[i].score);
}
printf("***********\n");
}
int main(int argc,char** argv){
stu_t DB[512] = {0};//结构体数组
int len = 0;
loadStu(DB,&len);
insertStu(DB,&len);
insertStu(DB,&len);
insertStu(DB,&len);
saveStu(DB,len);
printStu(DB,len);
return 0;
}
第二组:fputs/fgets(小段读取)
fputs和fgets的函数声明的原型是一样的,功能不一样
fputs和fgets属于行读取,他只会把一行的内容放入缓存区
fputs
函数名fputs
函数参数:const char *str, FILE *stream
函数功能:把字符串(str)写入到指定的流( stream) 中,但不包括空字符。指定的文件写入一个字符串(不自动写入字符串结束标记符‘\0’)。成功写入一个字符串后,文件的位置指针会自动后移
函数返回值:返回值为非负整数,出错则返回EOF(符号常量,其值为-1)。
简单理解就是把内存中的数据按格式输出到文件中
#include<stdio.h>
int main(int argc,char** argv){
FILE* fp = fopen("./test3.txt","w");
fputs("你好中国\n中国你好\n",fp);
fclose(fp);
return 0;
}
fgets
函数名:fgets
函数参数:char *str, int n, FILE *stream
函数功能:从指定的流 stream 读取一行,并把它存储在 str 所指向的字符串内。当读取 (n-1) 个字符时,或者读取到换行符时,或者到达文件末尾时,它会停止,具体视情况而定。
函数返回值:遇到文件结尾和错误的时候,返回一个空指针,在代码中也可以用数字0来代替。如果一切正常,返回与传入的第一个参数相同的地址(char *)。
简单理解就是从文件中读取(n-1)个字符到指定内存中
#include<stdio.h>
int main(int argc,char** argv){
char buf[50] = {0};
char* temp = 0;
FILE* fp = fopen("./test.txt","r");
while(1){
//循环调用fgets将整个文件读取完毕
temp = fgets(buf,50,fp);
if(temp == NULL){break;}
printf("%s",buf);
}
fclose(fp);
return 0;
}
使用fgets和fputs实现shell指令中的cp功能
函数名: lstat
功 能: 获取一些文件相关的信息
用 法: int lstat(const char *path, struct stat *buf);
参数:path:文件路径名。buf:结构体的指针,这个结构体的定义在头文件sys/stat.h
/*
使用fgets和fputs实现shell指令中的cp功能
第一点:需要获取到终端上输入的文件的名字,并且在代码中使用
例如:cp+f1+f2
那么用r的方式打开f1并且用fgets读取,读取一次后,用w的方式打开f2并用fputs输出。最后全都完成后将f1和f2关闭
追加功能:如果f2是一个目录文件,则直接将f1原名拷贝f2中
*/
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int main(int argc,char** argv){//argc统计参数个数,(char*)argv[]存储参数名字
if(argc<3){
printf("cp: 缺少了文件操作数\nTry 'cp --help' for more information.\n");
return 0;
}
char buf[10] = {0};
char* retval = 0;
char tarfile[50] = "./";
struct stat temp;//结构体
lstat(argv[2],&temp);//获取一些文件相关的信息
if(S_ISDIR(temp.st_mode)){//判断是否为.开头
//假如argv[1]中的数据为demo1.c
//假如argv[2]中的数据为111
strcat(tarfile,argv[2]);//./111
strcat(tarfile,"/");//./111/
strcat(tarfile,argv[1]);//./111/demo1.c
}else{
strcpy(tarfile,argv[2]);
}
FILE* rfp = fopen(argv[1],"r");//fopen的灵活使用
FILE* wfp = fopen(tarfile,"w");
while(1){
retval = fgets(buf,10,rfp);//打开文件后读取10个字符到字符串,要函数返回值的目的是判断是否读取完毕
if(retval == NULL){break;}
fputs(buf,wfp);//打开文件后把字符串中的数据输出到文件中
}
fclose(rfp);
fclose(wfp);
return 0;
}
第三组:fwrite/fread(模块读取,结构体中好用)
fwrite 和 fread的函数声明的原型是一样的,功能正好相反
函数名:fwrite
函数参数:void* ptr,int size,int num,FILE* fp
解释:将ptr所指向的地址作为首地址,然后将首地址以及之后的若干个地址封装成整个模块,一个模块size个字节,最后将总共num个模块写入到fp所指向的文件中去的
fwrite和fread的使用
#include<stdio.h>
#include<string.h>
typedef struct Data{
int age;
char name[20];
int score;
}data_t;
int main(){
data_t st1 = {20,"张三",80};
data_t st2 = {0};
//将st所指向的地址作为首地址,
//然后将首地址以及之后的个地址封装成整个模块,
//一个模块sizeof(st)个字节,最后将总共1个模块写入到wfp所带表的文件中去的
FILE* wfp = fopen("test4.txt","w");
fwrite(&st1,sizeof(st1),1,wfp);
fclose(wfp);
FILE* rfp = fopen("test4.txt","r");
fread(&st2,sizeof(st2),1,rfp);
fclose(rfp);
printf("%d\n%s\n%d\n",st2.age,st2.name,st2.score);
return 0;
}
使用fwrite和fread实现shell指令中的cp功能
/*
使用fread和fwrite实现shell指令中的cp功能
第一点:需要获取到终端上输入的文件的名字,并且在代码中使用
例如:cp+f1+f2
那么用r的方式打开f1并且用fread读取,读取一次后,用w的方式打开f2并用fwrite输出。最后全都完成后将f1和f2关闭
追加功能:如果f2是一个目录文件,则直接将f1原名拷贝f2中
*/
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int main(int argc,char** argv){
if(argc<3){
printf("cp: 缺少了文件操作数\nTry 'cp --help' for more information.\n");
return 0;
}
char buf[10] = {0};
int retval = 0;
char tarfile[50] = "./";
struct stat temp;
lstat(argv[2],&temp);
if(S_ISDIR(temp.st_mode)){
strcat(tarfile,argv[2]);
strcat(tarfile,"/");
strcat(tarfile,argv[1]);
}else{
strcpy(tarfile,argv[2]);
}
FILE* rfp = fopen(argv[1],"r");
FILE* wfp = fopen(tarfile,"w");
while(1){
//将rfp所指向的文件首地址以及之后的个地址封装成整个模块,
//一个模块1个字节,最后将总共1个模块写入到指针buf所指向的内存中
//(就是从文件一个一个字节读取)
retval = fread(buf,1,1,rfp);
if(retval == 0){break;}
fwrite(buf,1,1,wfp);//将指针buf所指向的内存中的数据,一个一个字节写入到文件中
}
fclose(rfp);
fclose(wfp);
return 0;
}
例如上面的代码的文件名为demo1.c,把demo1.c复制到当前目录下,并且命名为test.c
输入:./a.out demo1.c test.c
把demo1.c复制到名为a的目录下
输入:./a.out demo1.c ./a
使用fwrite和fread完成学生数组的加载保存
/*
编写一个加载保存学生信息的程序:
要求:先手动完善3个学生的信息,然后将3个学生添加入数组中。
将整个数组保存到文件中去
从文件中读取所有信息进入整个数组
要求:当第一次运行完成个程序后,第二次再次运行程序,同名学生添加失败
学生信息有:姓名,年龄,成绩,要求姓名唯一
*/
#include<stdio.h>
#include<string.h>
typedef struct Stu{
char name[20];
int age;
int english;
int chinese;
int math;
int physics;
int chemics;
int bio;
}stu_t;
void insertStu(stu_t* DB,int* len){
char name[20] = {0};
int age = 0;
int english;
int chinese;
int math;
int physics;
int chemics;
int bio;
printf("请输入学生的姓名,年龄:\n");
scanf("%s %d",name,&age);
printf("请输入 语数外物化生 成绩:\n");
scanf("%d %d %d %d %d %d",&english,&chinese,&math,&physics,&chemics,&bio);
while(getchar()!=10);
stu_t st = {0};
strcpy(st.name,name);
st.age = age;
st.english = english;
st.chinese = chinese;
st.math = math;
st.physics = physics;
st.chemics = chemics;
st.bio = bio;
DB[*len] = st;
(*len)++;
}
void saveStu(stu_t* DB,int len){
FILE* fp = fopen("./stuDB.txt","w");
int i = 0;
for(i=0;i<len;i++){
fwrite(DB+i,sizeof(stu_t),1,fp);
}
fclose(fp);
}
void loadStu(stu_t* DB,int* len){
FILE* fp = fopen("./stuDB.txt","r");
if(fp == NULL){return;}
int retval = 0;
int i = 0;
while(1){
retval = fread(DB+i,sizeof(stu_t),1,fp);
if(retval == 0){break;}
(*len)++;
}
fclose(fp);
}
void printStu(stu_t* DB,int len){
int i = 0;
for(i=0;i<len;i++){
printf("***********\n");
printf("姓名:%s\n",DB[i].name);
printf("年龄:%d\n",DB[i].age);
printf("英语:%d\n",DB[i].english);
printf("语文:%d\n",DB[i].chinese);
printf("数学:%d\n",DB[i].math);
printf("物理:%d\n",DB[i].physics);
printf("化学:%d\n",DB[i].chemics);
printf("生物:%d\n",DB[i].bio);
}
printf("***********\n");
}
int main(int argc,char** argv){
stu_t DB[512] = {0};
int len = 0;
loadStu(DB,&len);
insertStu(DB,&len);
saveStu(DB,len);
printStu(DB,len);
return 0;
}