1. 重定向
重定向文件输出:
把运行出来的内容直接保存在文件中 ./a.out > hello.txt
#include <stdio.h>
int main(){
printf("hallo\n");
}
重定向执行结果:
[admin@localhost cfile]$ ./hi > hi.txt
[admin@localhost cfile]$ cat hi.txt
hallo
重定向文件输入:
把文件中的内容给运行后的输入 ./a.out < hello.txt
#include <stdio.h>
int main(){
char name[20];
scanf("%s",name);
printf("hallo %s\n",name);
}
重定向执行结果:
[admin@localhost cfile]$ ./hi < hi.txt
hallo hallo
其他执行情况:
[admin@localhost cfile]$ ./hi > hi.txt
123
[admin@localhost cfile]$ cat hi.txt
hallo 123
[admin@localhost cfile]$ ./hi >> hi.txt
456
[admin@localhost cfile]$ cat hi.txt
hallo 123
hallo 456
[admin@localhost cfile]$ echo abc | ./hi
hallo abc
结构体存:
结构体输出重定向到文件中
#include <stdio.h>
typedef struct Student{
char name[20];
int age;
float score;
}Stud;
int main(){
Stud s1 = {"张三",23,98};
printf("%s %d %f\n",s1.name,s1.age,s1.score);
}
结果为:
[admin@localhost cfile]$ ./hi1 > hi1.txt
[admin@localhost cfile]$ cat hi1.txt
张三 23 98.000000
结构体取:
结构体格式的文件内容重定向到程序输入中
#include <stdio.h>
typedef struct Student{
char name[20];
int age;
float score;
}Stud;
int main(){
Stud s1;
scanf("%s%d%f",&s1.name,&s1.age,&s1.score);
printf("%s %d %f\n",s1.name,s1.age,s1.score);
}
结果为:
[admin@localhost cfile]$ ./hi1 < hi1.txt
张三 23 98.000000
[admin@localhost cfile]$ ./hi1
李四 24 92.13
李四 24 92.129997
[admin@localhost cfile]$ ./hi1 > hi1.txt
李四 24 92.13
[admin@localhost cfile]$ cat hi1.txt
李四 24 92.129997
每次重定向输入会替换文件中的内容
2. 读文件和写文件
fscanf() 写文件
fprintf() 读文件
返回实际读取的数据个数,出错或者到结尾返回 EOF
stdin是标准读,从终端读取
stdout是标准写,从终端写
在终端写入文件后直接在终端读出
#include <stdio.h>
typedef struct Student{
char name[20];
int age;
float score;
}Stud;
int main(){
Stud s1;
fscanf(stdin,"%s%d%f",&s1.name,&s1.age,&s1.score);
fprintf(stdout,"%s %d %f\n",s1.name,s1.age,s1.score);
}
结果为:
张美丽 23 97
张美丽 23 97.000000
3. 打开文件和关闭文件
fopen() 打开文件
fclose() 关闭文件
基本框架:
FILE* fp = fopen("文件路径","打开方式");
if(NULL != fp){
fclose(fp);
}
打开方式:
NO. |
打开方式 |
含义 |
1 |
r |
读(默认文本文件) |
2 |
w |
写,如果这个文件不存在,就创建这个文件;如果这个文件存在,则清空内容 |
3 |
a |
追加,如果这个文件不存在,就创建这个文件;如果这个文件存在,则在文件尾部追加内容 |
4 |
+ |
以可读可写的方式打开文件,配合r、w、a使用 |
5 |
t |
文本文件 |
6 |
b |
二进制文件 |
FILE是一种基于文件结构的变量类型,FILE* fp; 声明了 fp 是 FILE 型指针
读取文件中的学生信息:
#include <stdio.h>
typedef struct Student{
char name[20];
int age;
float score;
} Stud;
int main(){
FILE* pfile = fopen("hi1.txt","r"); //打开文件
if(NULL == pfile){
printf("file is not existed!\n");
return 1;
}
int n;
fscanf(pfile,"%d",&n);
Stud s[n];
for(int i=0;i<n;++i){
fscanf(pfile,"%s%d%f",&s[i].name,&s[i].age,&s[i].score);
}
fclose(pfile); //关闭文件
for(int i=0;i<n;++i){
fprintf(stdout,"%s %d %f\n",s[i].name,s[i].age,s[i].score);
}
}
在 hi1.txt 文件中输入内容:
2
李四 24 92.129997
王五 23 90
输出结果为:
李四 24 92.129997
王五 23 90.000000
4. 综合大题:学生信息管理系统
功能:
进入页面显示文件内容加载情况,选择数字进入不同功能
- 查看所有学生信息
- 录入学生信息
- 按成绩排序
- 查询学生成绩
- 删除学生信息
- 以上功能完成则继续选择,输入0才退出
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
typedef struct Student{
char name[20];
int age;
float score;
}Stud;
//从文件中加载学生信息
bool Load(const char* path,Stud s[],int* n){ //文件,读出来放s,个数
FILE* pfile = fopen(path,"r"); //只读方式打开文件
if(NULL == pfile){
printf("file is not existed!\n");
return false;
}
// 读取学生个数
int m;
fscanf(pfile,"%d",&m);
*n = m;
for(int i=0;i<m;++i){
fscanf(pfile,"%s%d%f",&s[i].name,&s[i].age,&s[i].score);
}
fclose(pfile); //关闭文件
pfile = NULL; //把指针清空,防止后续代码误用
return true;
}
//把学生数据保存到文件中
bool Save(const char* path,const Stud* s,int n){
FILE* pfile2 = fopen(path,"w"); //只写方式打开文件
if(NULL == pfile2){
printf("file stud.txt is not existed!\n");
return false;
}
fprintf(pfile2,"%d\n",n);
for(int i=0;i<n;++i){
fprintf(pfile2,"%s %d %f\n",s[i].name,s[i].age,s[i].score);
}
fclose(pfile2); //关闭文件
pfile2 = NULL; //把指针清空,防止后续代码误用
return true;
}
//录入新信息
void Register(Stud* s,int*n){
printf("请输入学生个数:");
//新加学生个数
int m;
scanf("%d",&m);
//新建一个表,并写入
Stud t[m];
for(int i=0;i<m;++i){
scanf("%s%d%f",&t[i].name,&t[i].age,&t[i].score);
}
//把新信息加在原数据的后面
for(int i=0;i<m;++i){
s[*n+i] = t[i];
}
*n += m;
}
//显示所有已登记的所有学生信息
void ShowStudents(const Stud s[],int n){
printf("姓名\t年龄\t成绩\n");
for(int i=0;i<n;++i){
printf("%s\t%d\t%f\n",s[i].name,s[i].age,s[i].score);
}
}
//按成绩排序
int ScoreCmp(const void* a,const void* b){
return((Stud*)b)->score - ((Stud*)a)->score;
}
void SortStudents(Stud s[],int n){
qsort(s,n,sizeof(Stud),ScoreCmp); //ScoreCmp为回调函数,qsort用来排序
}
//查找学生信息
void SearchStudent(const Stud s[],int n){
printf("请输入学生姓名:");
//先输一个名字给name[20]
char name[20];
scanf("%s",name);
bool found = false; //标记,假设没找到
for(int i=0;i<n;++i){
if(strcmp(s[i].name,name)==0){
found = true; //找到了,修改标记
printf("查询结果:");
printf("%s\t%d\t%f\n",s[i].name,s[i].age,s[i].score);
}
}
if(!found){
printf("查无此人\n");
}
}
//删除学生信息
void DeleteStudent(Stud s[],int *n){
printf("请输入学生姓名:");
//先输一个名字给name[20]
char name[20];
scanf("%s",name);
for(int i=0;i<*n;++i){
//如果找到这个学生的名字
if(strcmp(s[i].name,name)==0){
for(int j=i;j<*n;++j){
s[j]=s[j+1]; //直接把后面的信息补上来,替换掉
}
--*n;
}
}
}
//主函数
int main(){
const char* data = "./stud.txt";
Stud s[100];
int n;
if(Load(data,s,&n)){
printf("已经加载成功%d学生信息\n",n);
}
while(true){
printf("查看功能列表:\n");
printf("1.查看所有学生信息\n");
printf("2.录入学生信息\n");
printf("3.按成绩排序\n");
printf("4.查询学生成绩\n");
printf("5.删除学生信息\n");
printf("0.退出\n");
printf("请选择功能:");
int num;
scanf("%d",&num);
switch(num){
case 1:
ShowStudents(s,n);
break;
case 2:
Register(s,&n);
Save(data,s,n);
break;
case 3:
SortStudents(s,n);
ShowStudents(s,n);
break;
case 4:
SearchStudent(s,n);
break;
case 5:
DeleteStudent(s,&n);
Save(data,s,n);
break;
case 0:
return 0;
default:
printf("选项输入错误\n");
return 1;
}
}
}
文件存在且空时,结果为:
已经加载成功0学生信息
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:2
请输入学生个数:3
张三 25 85
李四 21 94
王五 23 91
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:1
姓名 年龄 成绩
张三 25 85.000000
李四 21 94.000000
王五 23 91.000000
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:3
姓名 年龄 成绩
李四 21 94.000000
王五 23 91.000000
张三 25 85.000000
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:4
请输入学生姓名:王五
查询结果:王五 23 91.000000
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:5
请输入学生姓名:李四
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:1
姓名 年龄 成绩
王五 23 91.000000
张三 25 85.000000
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:0
该程序在没有文件的情况下,如果录入信息,会新建文件 ./stud.txt
void Func(int arr[] , int n); 和 void Func(int* arr , int n); 两种写法完全一样
- char str[20] = “abcd”; 初始化字符数组
char* s = str; 字符串指针,存放地址(字符数组地址),可以修改
- char* s = “abcd”; 初始化字符串指针,存放地址(字符串常量地址),不可以修改字符
s[0] = ‘b’; 当修改变量时,前者可以修改,后者则吐核
5. 二进制读文件和二进制写文件
fread() 二进制读
fwrite() 二进制写
使用结构:
fread(变量地址,变量长度,读的个数,从该文件读);
fwrite(变量地址,变量长度,写的个数,写到的文件);
上述问题下可以直接更改写文件(保存文件)
//把学生数据保存到文件中
bool Save(const char* path,const Stud* s,int n){
FILE* pfile2 = fopen(path,"wb"); //只写方式打开文件
if(NULL == pfile2){
printf("file stud.txt is not existed!\n");
return false;
}
fwrite(&n,sizeof(n),1,pfile2);
//fprintf(pfile2,"%d\n",n);
//for(int i=0;i<n;++i){
// fprintf(pfile2,"%s %d %f\n",s[i].name,s[i].age,s[i].score);
//}
fwrite(s,sizeof(Stud),n,pfile2); //可以直接写结构体的内容
fclose(pfile2); //关闭文件
pfile2 = NULL; //把指针清空,防止后续代码误用
return true;
}
主函数需要重新建立一个文件用来存二进制信息,每一步都Save给新文件
运行 ···
新录入学生的信息会以二进制的形式保存在新的文件中,我们查看时看不懂的
保存了一些数据后,可以进行二进制读取的操作
bool Load(const char* path,Stud s[],int* n){ //文件,读出来放s,个数
FILE* pfile = fopen(path,"rb"); //只读方式打开文件
if(NULL == pfile){
printf("file is not existed!\n");
return false;
}
// 读取学生个数
int m;
fread(&m,sizeof(m),1,pfile);
//fscanf(pfile,"%d",&m);
*n = m;
//for(int i=0;i<m;++i){
// fscanf(pfile,"%s%d%f",&s[i].name,&s[i].age,&s[i].score);
//}
fread(s,sizeof(Stud),m,pfile);
fclose(pfile); //关闭文件
pfile = NULL; //把指针清空,防止后续代码误用
return true;
}
修改主函数中 Load 应加载的新文件
运行···
这样之前的功能就能照常使用了,只不过文件中的二进制内容我们看不懂
用二进制读写文件做学生信息管理系统
整体程序可参考文章《C语言进阶:学生信息管理系统的不断优化,大块程序拿去用》第2部分
6. 文件定位
获取位置(在本文第几个字节): ftell(文件指针)
设置位置(指向第几个字节): fseek(文件指针,偏移量,起始点)
起始点 |
含义 |
代表数值 |
SEEK_SET |
从头开始 |
0 |
SEEK_CUR |
从当前开始 |
1 |
SEEK_END |
从结束开始 |
2 |
查看多个文件的大小(字节数):
#include <stdio.h>
int main(int argc,char* argv[]){
if(argc == 1){
printf("error:no argument\n");
printf("Usage:%s filepath ...",argv[0]);
return 1;
}
for(int i=1;i<argc;++i){
FILE* pf = fopen(argv[i],"r");
if(NULL == pf){
printf("error:open %s fail\n",argv[i]);
return 1;
}
fseek(pf,0,SEEK_END);
printf("%s %d\n",argv[i],ftell(pf));
fclose(pf);
pf = NULL;
}
}
结果为:
[admin@localhost cfile]$ ./sizeof ../note/*
../note/2021-3-1 LU1笔记 4145
../note/2021-3-2 LU2笔记 7513
../note/2021-3-4LU3笔记 2028
../note/C1 5714
../note/C10 2318
../note/C11 443
../note/C2 7808
../note/C3 8972
文件定位函数的应用:
当数据量很大时,用文件定位,每次修改信息时,不用重新刷新信息
把指针指在要改学生信息的开头:
fseek(fp , sizeof(int)+sizeof(Stud)*i , SEEK_SET);
优化学生管理系统:用文件定位函数增加学生信息、修改学生信息
详细应用请看文章《C语言进阶:学生信息管理系统的不断优化,大块程序拿去用》第2部分
7. 其他文件操作函数
文件结尾判断
feof(pf) pf 指针是否在文件结尾处
读完文件返回1,没读完就返回0
学生信息文件中不写学生个数,只用 n 做统计:
//从文件中加载学生信息
bool Load(const char* path,Stud s[],int* n){ //文件,读出来放s,个数
FILE* pfile = fopen(path,"r"); //只读方式打开文件
if(NULL == pfile){
printf("file is not existed!\n");
return false;
}
// 读取学生个数
int i=0;
for(;;){ // while(!feof(pfile)){ 到文件结尾处才停止
int res = fscanf(pfile,"%s%d%f",&s[i].name,&s[i].age,&s[i].score);
if(EOF == res) break;
++i;
}
*n = i; // *n = i-1; 表示feof读多了一个值,所以要减1
fclose(pfile); //关闭文件
pfile = NULL; //把指针清空,防止后续代码误用
return true;
}
//把学生数据保存到文件中
bool Save(const char* path,const Stud* s,int n){
FILE* pfile2 = fopen(path,"w"); //只写方式打开文件
if(NULL == pfile2){
printf("file stud.txt is not existed!\n");
return false;
}
for(int i=0;i<n;++i){
fprintf(pfile2,"%s %d %f\n",s[i].name,s[i].age,s[i].score);
}
fclose(pfile2); //关闭文件
pfile2 = NULL; //把指针清空,防止后续代码误用
return true;
}
返回文件开头
rewind(pf) 返回文件开头
效果和 fseek(pf,0,SEEK_SET); 一样
清空数据流
fflush(fp) 清空数据流
文件重命名和文件删除
int rename(const char* 旧文件名 , const char* 新文件名); 文件重命名
int remove(char* 要删除的文件名); 文件删除
8. 系统再优化之用户登录与注册
增加用户注册和登录功能:
结构体 User 定义用户信息
main 函数在进入功能时要更改
加 bool RegisterUser 函数用于注册
加 bool Login 函数用于登陆
以下程序为有关注册和登录的部分程序:
//用户信息所用结构体
typedef struct User{
char name[20];
char password[6]; //密码6位
} User;
//录入注册信息
bool RegisterUser(const char* path){
printf("首次登陆需要注册\n");
User current;
printf("注册用户名:");
scanf("%s",current.name);
printf("注册密码:");
scanf("%s",current.password);
FILE* pf = fopen(path,"w");
if(NULL == pf){
return false;
}
fprintf(pf,"%s %s",current.name,current.password);
fclose(pf);
pf = NULL;
return true;
}
//验证用户和密码是否正确
bool Login(const char* path){
FILE* pf = fopen(path,"r");
if(NULL == pf){ //如果没存过用户信息,则存用户信息
return RegisterUser(path); //第一次登陆要注册,注册成功直接进入
}
printf("Login...\n");
User current;
printf("请输入用户名:");
scanf("%s",current.name);
printf("请输入密码:");
scanf("%s",current.password);
for(;;){
User user;
int res = fscanf(pf,"%s%s",user.name,user.password);
if(EOF == res) break;
if(strcmp(current.name,user.name)==0 && strcmp(current.password,user.password)==0){return true;}
}
return false;
}
//主函数
int main(){
const char* data = "./stud.txt";
const char* user = "./user.txt";
if(!Login(user)){
printf("error login\n");
return 1;
}
Stud s[100];
int n;
if(Load(data,s,&n)){
printf("已经加载成功%d学生信息\n",n);
}
while(true){
printf("查看功能列表:\n");
printf("1.查看所有学生信息\n");
printf("2.录入学生信息\n");
printf("3.按成绩排序\n");
printf("4.查询学生成绩\n");
printf("5.删除学生信息\n");
printf("0.退出\n");
printf("请选择功能:");
int num;
scanf("%d",&num);
switch(num){
case 1:
ShowStudents(s,n);
break;
case 2:
Register(s,&n);
Save(data,s,n);
break;
case 3:
SortStudents(s,n);
ShowStudents(s,n);
break;
case 4:
SearchStudent(s,n);
break;
case 5:
DeleteStudent(s,&n);
Save(data,s,n);
break;
case 0:
return 0;
default:
printf("选项输入错误\n");
return 1;
}
}
}
整体运行结果为:
[admin@localhost cfile]$ ./file
首次登陆需要注册
注册用户名:abcd
注册密码:987654
已经加载成功1学生信息
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:0
[admin@localhost cfile]$ ./file
Login...
请输入用户名:abcd
请输入密码:123456
error login
[admin@localhost cfile]$ ./file
Login...
请输入用户名:abcd
请输入密码:987654
已经加载成功1学生信息
查看功能列表:
1.查看所有学生信息
2.录入学生信息
3.按成绩排序
4.查询学生成绩
5.删除学生信息
0.退出
请选择功能:0
整体程序请看文章《C语言进阶:学生信息管理系统的不断优化,大块程序拿去用》第3部分