一.前言
ok啊,很是怠惰,好久没写博客了。回忆了一下最近,从期末实践周开始到结束之后的几周里,逐渐在游戏里迷失了,忘记了提升自我,这非常dangerous,所以在完成今天博客收工之后,我会仔细想想接下来的路该怎么走,毕竟每个人都要对自己的人生负责不是吗?Of course!
好好好,言归正传,这篇博客是为了复盘一下笔者期末实践周写的C语言报告第五部分,就是简单实现全球恐怖分子嫌疑人管理数据库系统。以下是要求:
期末报告有三个选题,有一个是对csv文件的操作,诸如增删改查之类,笔者觉得可以和第一个选题结合起来写。因为大多数人都选择对txt文件进行操作,但是笔者想整点不一样的,那么,action!
附一下选题三的要求:
二.实现
首先,理清一下思路:
1.登录
1).代码
登录操作的实现很简单,就是把你输入的东西和存储账户的csv文件内容进行匹配就行
先进行读取操作
#define ACCOUNT "账户.csv"
#define MAX_LINES 20 // 假设最多有20行
#define MAX_LENGTH 20 // 用户名和密码的最大长度
void read(char names[][MAX_LENGTH],char passwords[][MAX_LENGTH],int *count) {
FILE* fp=fopen(ACCOUNT, "r+");
if(fp==NULL){
printf("文件%s打开失败\n",ACCOUNT);
return;
}
*count=0;
while(*count<MAX_LINES&&fscanf(fp,"%19[^,],%19[^\n]",names[*count],passwords[*count])==2){
fgetc(fp);
(*count)++;
}
if(!feof(fp))printf("Fail to read data!\n");
fclose(fp);
}
接着内容匹配
int login(char n[][MAX_LENGTH],char p[][MAX_LENGTH],int count){
char name[MAX_LENGTH],password[MAX_LENGTH];
printf("\t全球恐怖分子嫌疑人管理数据库系统\t\n");
printf("账号:");
scanf("%19s",name);
printf("密码:");
fflush(stdin);
scanf("%19s",password);
fflush(stdin);
printf("\n");
printf("正在登录,请稍后...\n");
Sleep(2000);
int found=0;
for(int i=0;i<count;i++){
if(strcmp(name,n[i])==0&&strcmp(password,p[i])==0){
found=1;
break;
}
}
if(found){
return 1;
}else{
printf("该账号不存在或账户密码错误!");
return 0;
}
}
2).复盘
i.用法
写这一块代码的时候,确实遇到了蛮多的问题,因为笔者是分开来写的嘛,有的时候在单独的程序里跑的时候并不会出现问题,但是放到主程序里就会出现。
首先复习一下文件读取模式:
w | 如果文件不存在会创建,存在的话会覆盖原有内容(只能写不能读) |
w+ | 比w多了读的功能,仍然覆盖 |
r | 文件不存在会报错,只能读 |
r+ | 比r多了写的功能,文件不存在会报错 |
a | 文件不存在会报错,文件末尾追加 |
PS:除a外,文件指针默认位于文件开头
另外fseek、ftell、rewind等指针操作方式可以看:
C语言——文件操作函数 fseek、ftell、rewind详解-CSDN博客
接着复盘一下fscanf,scanf,fprintf,printf:
- scanf
用于从标准输入(通常是键盘)读取格式化输入。
原型:int scanf(const char *format, ...);
用法就不赘述了
- fscanf
与scanf
类似,但它从指定的文件流(如文件、字符串等)中读取格式化输入,而不是从标准输入。(就是从文件中读取)
原型:int fscanf(FILE *stream, const char *format, ...);
用法:fscanf(fp, "%d", &value);
- printf
用于向标准输出(通常是屏幕)发送格式化的输出。
原型:int printf(const char *format, ...);
用法就不赘述了
- fprintf
与printf
类似,但它将格式化的输出发送到指定的文件流,而不是标准输出。(写入文件)
原型:int fprintf(FILE *stream, const char *format, ...);
用法:fprintf(fp, "Value: %d\n", value);
那么”%19[^,],%19[^\n]“这一串是什么东西,是格式说明符,19说明最多匹配19个字符,%[^,]是匹配除','以外的字符,到第一个逗号为止,第二个同理,个人觉得和strtok函数功能有点类似,也有点像正则表达式
ii.遇到的问题
写一下遇到的问题
首当其冲啊,就是这个扫描集和标准缓冲区的问题,这是没修改之前的代码
while(*count<MAX_LINES&&fscanf(fp,"%19[^,],%19[^\n]",names[*count],passwords[*count])==2){
(*count)++;
}
把names这个数组输出来看一下,发现从第二个账户开始,账户名的开头都多了一个换行符,这就导致笔者在输入第二个及之后的账户密码都会显示该账号或密码错误,这是为什么呢,当时由于答辩的比较匆忙,也没有想出什么头绪,但现在复盘的时候解决了
看到这个换行符,我的第一反应就是缓冲区的问题,事实上也确实是,%19[^\n]虽然是会到\n为止,但是会在缓冲区留下一个\n,所以要把它跳过,即fgetc()函数。 那为什么不使用fflush(stdin)呢,首先,fflush()
函数是设计用来清空输出缓冲区(即刷新输出缓冲区)的,以确保所有缓冲的输出都被物理地写入到目标设备中。但是,对于输入缓冲区(如 stdin
),C标准并没有规定 fflush()
的行为。实际上,在大多数C语言实现中,对 stdin
使用 fflush()
是没有效果的,因为输入缓冲区不是由 fflush()
来管理的。在某些编译器上是可以使用的,比如说dev,但gcc上就不行,但是都可以用getchar或者fgetc替代。
2.功能
首先呢,以下大部分操作都是基于缓冲区--->文件的模式来完成的,所以要进行一个读取文件内容到缓冲区的操作:
void Buffer(char a[][100], int *num_rows) {
FILE* fp=fopen(PATH,"r");
if (fp==NULL){
printf("文件%s打开失败\n",PATH);
return;
}
char row[100];
while (fgets(row,100,fp)!=NULL) {
// printf("%s",row);
}
rewind(fp);
int col = 0;
char buff[1024];
while (fgets(buff,sizeof(buff),fp)!=NULL) {
buff[strcspn(buff,"\n")]=0;
strcpy(a[col],buff);
col++;
if(col>= 100){
printf("越界!\n");
break;
}
}
strcpy(a[col],"\0");
*num_rows=col;
fclose(fp);
}
1.显示
逐行显示
void show(char content[][100], int num_rows){
Sleep(1000);
for(int i=1;i<num_rows;i++){
printf("%s\n",content[i]);
}
Sleep(1000);
}
2.增
因为只是简单实现增加的功能,所以只实现的是末尾追加,像某两个人之间增加,或者莫一位置上增加的代码就没有写
void add(){
Sleep(1000);
char input[100];
printf("请依次输入要增加的嫌疑人的序号/姓名/性别/国籍\n");
FILE* fp=fopen(PATH,"a");
if(fp==NULL){
printf("文件%s打开失败\n",PATH);
return;
}
if (fgets(input, sizeof(input), stdin) != NULL) {
input[strcspn(input, "\n")] = 0;
fputs(input, fp);
fputs("\n", fp);
}
fclose(fp);
}
3.删
这里笔者借鉴了别人的博客,同样采取的是笨办法,是对缓冲区的文件进行修改在重新写入文件
void del(char content[][100],int num_rows){
char input[100];
char store[num_rows][100];
int find=0;
for (int i=0;i<num_rows;i++) {
strcpy(store[i],content[i]);
}
printf("请提供被删除人的编号或姓名:\n");
fgets(input,sizeof(input),stdin);
fflush(stdin);
input[strcspn(input, "\n")]=0;
for(int i=1;i<num_rows;i++){
char* token=strtok(content[i],",");//序号
char* name=strtok(NULL,",");//名
if (token!=NULL&&(strcmp(token,input)==0||strcmp(name,input)==0)){
strcpy(store[i],"$");
find=1;
break;
}
}
if(!find)printf("未查询到该人员讯息!\n");
for (int i = 0; i < num_rows; i++) {
printf("%s\n", store[i]);
}
FILE *fp=fopen(PATH,"w");
if(fp==NULL){
printf("文件%s打开失败\n",PATH);
return;
}
for(int i=0;i<num_rows;i++){
if(strcmp(store[i],"$")==0){
// printf("skip");
}
else fprintf(fp,"%s\n",store[i]);
}
fclose(fp);
}
4.查
void search(char content[][100], int num_rows){
char input[100];
char store[num_rows][100];
int find=0;
for (int i=0;i<num_rows;i++) {
strcpy(store[i],content[i]);
}
printf("请输入要查询的嫌疑人姓名:\n");
if (fgets(input, sizeof(input), stdin) != NULL) {
input[strcspn(input,"\n")] = 0;
}
fflush(stdin);
printf("正在为您查询,请稍后...\n");
Sleep(2000);
for(int i=1;i<num_rows;i++){
strtok(content[i],",");
char* name=strtok(NULL,",");
if(strcmp(name,input)==0)printf("已为您查询到该嫌疑犯的信息\n\n序号/姓名/性别/国籍\n%s\n",store[i]),find=1;
}
if(!find)printf("未查询到该嫌疑犯的信息!请检查输入的信息是否有误\n");
}
5.统计
void Count(char content[][100], int num_rows){
Sleep(1000);
int count=0;
char input[100];
printf("请输入要统计的国别:\n");
if (fgets(input, sizeof(input), stdin) != NULL) {
input[strcspn(input, "\n")] = 0;
}
fflush(stdin);
for(int i=1;i<num_rows;i++){
strtok(content[i],",");
strtok(NULL,",");
strtok(NULL,",");
char* nation=strtok(NULL,",");
if(strcmp(nation,input)==0)count++;
}
printf("该国的嫌疑犯有%d人\n",count);
}
6.改
这里改的功能作者没有写,改和删的实现没有什么区别,都是找到匹配对象后修改,然后写入文件
3.主函数
int main(){
int is_exit;
char names[MAX_LINES][MAX_LENGTH];
char passwords[MAX_LINES][MAX_LENGTH];
int count=0;
read(names, passwords, &count);
char content[100][100];
int num_rows,option;
Buffer(content, &num_rows);
// search(content,num_rows);
if((is_exit=login(names,passwords,count))==1){
while((option=menu())!=6){
printf("正在执行操作%d\n\n",option);
if(option==1){
show(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
}
if(option==2){
del(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
if(option==3){
add();
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
if(option==4){
search(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
if(option==5){
Count(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
}
}
return 0;
}
4.全部代码
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<windows.h>
#define MAX_LINES 20 // 假设最多有20行
#define MAX_LENGTH 20 // 用户名和密码的最大长度
#define ACCOUNT "账户.csv"
#define PATH "全球恐怖分子嫌疑人管理数据库系统.csv"
void read(char names[][MAX_LENGTH],char passwords[][MAX_LENGTH],int *count) {
FILE* fp=fopen(ACCOUNT, "r+");
if(fp==NULL){
printf("文件%s打开失败\n",ACCOUNT);
return;
}
*count=0;
while(*count<MAX_LINES&&fscanf(fp,"%19[^,],%19[^\n]",names[*count],passwords[*count])==2){
// printf("name:%s\n", names[*count]);
fgetc(fp);
(*count)++;
}
if(!feof(fp))printf("Fail to read data!\n");
fclose(fp);
}
int login(char n[][MAX_LENGTH],char p[][MAX_LENGTH],int count){
char name[MAX_LENGTH],password[MAX_LENGTH];
printf("\t全球恐怖分子嫌疑人管理数据库系统\t\n");
printf("账号:");
scanf("%19s",name);
printf("密码:");
fflush(stdin);
scanf("%19s",password);
fflush(stdin);
printf("\n");
printf("正在登录,请稍后...\n");
Sleep(2000);
int found=0;
for(int i=0;i<count;i++){
// printf("Name: %s\n",n[i]);
if(strcmp(name,n[i])==0&&strcmp(password,p[i])==0){
found=1;
break;
}
}
if(found){
return 1;
}else{
printf("该账号不存在或账户密码错误!");
return 0;
}
}
int menu(){
int i;
printf("**********\n1 显示名单\n2 删除嫌疑人\n3 新增嫌疑人\n4 查询嫌疑人\n5 按国别统计\n6 退出登录\n**********\n");
printf("\n请选择要进行的操作:\n");
scanf("%d",&i);
fflush(stdin);
if(1<=i&&i<=6){
printf("操作成功!跳转中...\n");
Sleep(2000);
return i;
}else printf("error!");
}
void Buffer(char a[][100], int *num_rows) {
FILE* fp=fopen(PATH,"r");
if (fp==NULL){
printf("文件%s打开失败\n",PATH);
return;
}
char row[100];
while (fgets(row,100,fp)!=NULL) {
// printf("%s",row);
}
rewind(fp);
int col = 0;
char buff[1024];
while (fgets(buff,sizeof(buff),fp)!=NULL) {
buff[strcspn(buff,"\n")]=0;
strcpy(a[col],buff);
col++;
if(col>= 100){
printf("越界!\n");
break;
}
}
strcpy(a[col],"\0");
*num_rows=col;
fclose(fp);
}
void show(char content[][100], int num_rows){
Sleep(1000);
for(int i=1;i<num_rows;i++){
printf("%s\n",content[i]);
}
Sleep(1000);
}
void del(char content[][100],int num_rows){
char input[100];
char store[num_rows][100];
int find=0;
for (int i=0;i<num_rows;i++) {
strcpy(store[i],content[i]);
}
printf("请提供被删除人的编号或姓名:\n");
fgets(input,sizeof(input),stdin);
fflush(stdin);
input[strcspn(input, "\n")]=0;
for(int i=1;i<num_rows;i++){
char* token=strtok(content[i],",");//序号
char* name=strtok(NULL,",");//名
if (token!=NULL&&(strcmp(token,input)==0||strcmp(name,input)==0)){
strcpy(store[i],"$");
find=1;
break;
}
}
if(!find)printf("未查询到该人员讯息!\n");
for (int i = 0; i < num_rows; i++) {
printf("%s\n", store[i]);
}
FILE *fp=fopen(PATH,"w");
if(fp==NULL){
printf("文件%s打开失败\n",PATH);
return;
}
for(int i=0;i<num_rows;i++){
if(strcmp(store[i],"$")==0){
// printf("skip");
}
else fprintf(fp,"%s\n",store[i]);
}
fclose(fp);
}
void add(){
Sleep(1000);
char input[100];
printf("请依次输入要增加的嫌疑人的序号/姓名/性别/国籍\n");
FILE* fp=fopen(PATH,"a");
if(fp==NULL){
printf("文件%s打开失败\n",PATH);
return;
}
if (fgets(input, sizeof(input), stdin) != NULL) {
input[strcspn(input, "\n")] = 0;
fputs(input, fp);
fputs("\n", fp);
}
fclose(fp);
}
void search(char content[][100], int num_rows){
char input[100];
char store[num_rows][100];
int find=0;
for (int i=0;i<num_rows;i++) {
strcpy(store[i],content[i]);
}
printf("请输入要查询的嫌疑人姓名:\n");
if (fgets(input, sizeof(input), stdin) != NULL) {
input[strcspn(input,"\n")] = 0;
}
fflush(stdin);
printf("正在为您查询,请稍后...\n");
Sleep(2000);
for(int i=1;i<num_rows;i++){
strtok(content[i],",");
char* name=strtok(NULL,",");
if(strcmp(name,input)==0)printf("已为您查询到该嫌疑犯的信息\n\n序号/姓名/性别/国籍\n%s\n",store[i]),find=1;
}
if(!find)printf("未查询到该嫌疑犯的信息!请检查输入的信息是否有误\n");
}
void Count(char content[][100], int num_rows){
Sleep(1000);
int count=0;
char input[100];
printf("请输入要统计的国别:\n");
if (fgets(input, sizeof(input), stdin) != NULL) {
input[strcspn(input, "\n")] = 0;
}
fflush(stdin);
for(int i=1;i<num_rows;i++){
strtok(content[i],",");
strtok(NULL,",");
strtok(NULL,",");
char* nation=strtok(NULL,",");
if(strcmp(nation,input)==0)count++;
}
printf("该国的嫌疑犯有%d人\n",count);
}
int main(){
int is_exit;
char names[MAX_LINES][MAX_LENGTH];
char passwords[MAX_LINES][MAX_LENGTH];
int count=0;
read(names, passwords, &count);
char content[100][100];
int num_rows,option;
Buffer(content, &num_rows);
// search(content,num_rows);
if((is_exit=login(names,passwords,count))==1){
while((option=menu())!=6){
printf("正在执行操作%d\n\n",option);
if(option==1){
show(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
}
if(option==2){
del(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
if(option==3){
add();
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
if(option==4){
search(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
if(option==5){
Count(content,num_rows);
printf("操作成功!正在为您返回菜单...\n\n\n");
Sleep(1000);
Buffer(content, &num_rows);
}
}
}
return 0;
}
三.命令行操作
复盘一下命令行吧
1.基本操作
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("argc = %d\n", argc);
for (int i = 0; i < argc; ++i)
{
printf("argv[%d] =%s\n", i,argv[i]);
}
return 0;
}
运行结果如下:
从上述示例可以看到调用命令行的操作为:
在终端找到该可执行文件的文件夹所在路径,cmd打开,然后输入改可执行文件(xxx.exe)+参数总数+参数
- argc,agrv,main的关系
main
函数可以接受命令行参数,这些参数通过argc
和argv
这两个参数传递给main
函数。
- argc
这是一个整数,表示传递给程序的命令行参数的数量。请注意,argc
至少为 1,因为第一个参数(argv[0]
)总是可执行程序的名称(或者是启动程序的命令)。
- argv
这是一个字符指针数组(或字符串数组),每个元素都是一个指向以 null 结尾的字符串的指针。这些字符串代表传递给程序的命令行参数。argv[0]
是程序的名称,argv[1]
是第一个命令行参数,依此类推,直到argv[argc-1]
,它是最后一个命令行参数。argv[argc]
是一个空指针,用作数组的结束标志。
2.案例
1.统计字符数
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
// printf("argc = %d\n", argc);
int count=0;
for (int i = 1; i < argc; i++)
{
count+=strlen(argv[i]);
}
printf("该命令行的字符数为%d",count);
return 0;
}
运行如下:
2.文件拷贝
# include<stdio.h>
# include<string.h>
void read(char content[],char name[]){
FILE* fp=fopen(name,"r");
if(NULL==fp){
printf("Fail!");
return;
}
char ch=0;
int index=0;
while((content[index]=fgetc(fp))!=EOF){
++index;
}
content[index]='\0';
// printf("%s\n",content);
fclose(fp);
fp = NULL;
}
void write(char* s){
FILE* fp=fopen("demo2-copy.txt","w");
if(NULL==fp){
printf("Fail!");
return;
}
fputs(s,fp);
fclose(fp);
fp = NULL;
printf("写入成功!");
}
int main(int argc,char* argv[]){
char s[100],name[100];
printf("输入copy文件的相对路径/绝对路径:\n");
scanf("%99s",name);
read(s,name);
write(s);
return 0;
}
运行如下:
3. 多文件合并
# include<stdio.h>
# include<string.h>
void read(char content[],char *name){
FILE* fp=fopen(name,"r");
if(NULL==fp){
printf("Fail!");
return;
}
char ch=0;
int index=0;
while((content[index]=fgetc(fp))!=EOF){
++index;
}
content[index]='\0';
// printf("%s\n",content);
fclose(fp);
fp = NULL;
}
void write(char* s){
FILE* fp=fopen("demo-combine.txt","a");
if(NULL==fp){
printf("Fail!");
return;
}
fputs(s,fp);
fclose(fp);
fp = NULL;
// printf("写入成功!");
}
int main(int argc,char* argv[]){
char s[100],name[100];
if(argc>1){
for (int i = 1; i < argc; i++){
strcpy(name, argv[i]);
read(s,name);
write(s);
printf("第%d个文件拷贝成功!\n",i);
}
}else{
printf("输入copy文件的相对路径/绝对路径:\n");
scanf("%99s",name);
read(s,name);
write(s);
}
printf("合并完成!");
return 0;
}
运行如下:
四.总结
ok啊,这篇博客也是在笔者的写写停停下,总计两天也是完成了。上面写的那个系统呢还是有诸多的不足和漏洞的,比如说我删除了某一个序号的嫌疑人后呢,还不能识别缺少了哪一个序号,或者重新排序,但是这也是要基于系统的需求是怎么样的。然后呢,笔者为了让系统做的跟真的一样的,甚至还设置了休眠哈哈,非常的nice,笑死了哈哈哈哈,当然啦,在平常的学习中,都可以留意一下比较有意思的库,真的很有意思。甚至笔者在最初的构想里,是要设计出ui的,可以直观地操作,但是呢笔者花了一天(实际可能就半天多一点)去尝试使用hieasyX这个库,但是无果,所以这个念头不得不破产了。但是呢编程,最不怕的就是失败,而是不敢尝试,不敢多探索。
说一下后面的博客方向,笔者可能会开始写一些数据结构+算法,爬虫也会继续写下去。好了,下一篇博客见!Bye~