c与文件读写
对于通讯录系统进行一个改进 加上文件读写功能 成为真正意义上的通讯录
两种思路
增删改查分别对文件内容进行改写 代码工程量可能很大 但是程序执行起来需要改写的地方会少一些 对于数据量庞大的项目可以采用这种 但是一般都用连接数据库了 之前试过的连接mysql已经可以实现这种操作 就不再多此一举了 本来文件读写就是了解性的东西 实际用不太到了
然后一种思路就是后面摸索出来发现前面做了很多无用功 其实对于这种通讯录啊什么的 这种小数据的东西 直接在程序执行前 将文件里的内容全部读入到链表当中 然后再在链表中完成所有操作 最后将文件原有内容清空 用处理后的数据覆盖上去就OK了
对于之前的程序基本没什么改变 就是添加了一个读模块和写模块
接下来分析实现
准备
#define _CRT_SECURE_NO_WARNINGS
防止c语法中的文件读写函数报错
打开
首先在主函数中
FILE* fp;
变量 fp 是一个指向 FILE 结构体的指针,用于在代码中操作文件。在这里,fp 被赋值为 fopen() 函数的返回值,即文件句柄。如果文件打开成功,fp 将指向打开的文件;否则它将为 NULL。
(fp = fopen("text1.txt", "r")) == NULL
尝试打开名为 "text1.txt" 的文件,并将其句柄存储在变量 fp 中。函数 fopen() 的第一个参数是文件名,第二个参数是文件打开模式。在这里,模式是 "r",表示以只读模式打开文件。
如果 fopen() 函数返回值为 NULL,则说明文件打开失败,即该文件不存在或无法被打开。因此,代码使用等号运算符将 fp 的值与 NULL 进行比较,以检查文件是否已经成功打开。如果 fp 等于 NULL,意味着文件打开失败,条件表达式的结果为真,程序将进入 if 代码块执行创建文件的操作。否则,程序将跳过该 if 代码块,继续向下执行。
如果打开失败 有可能是因为该文件不存在 所以就调用CreateFile();函数去生成一个新文件
void CreateFile()
{
FILE* fp = NULL;
if ((fp = fopen("text1.txt", "w")) == NULL) {
printf("无法创建文件!\n");
exit(1);
}
else {
printf("文件创建成功!\n");
}
fclose(fp);
}
定义一个 FILE 类型的指针变量 fp,并将其初始化为 NULL。
使用fopen()函数以写入模式打开文件"text1.txt" 将其句柄存储在变量 fp 中
如果打开文件失败,则会打印错误消息并调用 exit() 函数以终止程序。
如果文件打开成功,函数打印一条成功创建文件的消息
并使用 fclose() 函数关闭文件
(以w模式即写入模式打开文件有创建效果 然后如果以w模式一打开就关闭 就会清除文件内所有数据 之后的清除操作原理也是如此 还有一个a模式 追加模式 可以理解为 w就是清空重新写 不写就等于清空 a是不清空 在原有数据后面继续加数据)
然后如果创建了该文件 再重新打开 还是失败的话 就是未知原因了 等待报错就好了
fopen()
第一个参数 filename 是一个字符串,表示要打开的文件的路径和文件名。如果文件位于当前目录下,则可以直接使用文件名,例如:"myfile.txt"。如果文件位于其他目录下,则需要使用文件的完整路径,例如:"C:\mydir\myfile.txt"(或者"C:\mydir\myfile.txt" 需要转义字符)
第二个参数 mode 是一个字符串,表示打开文件的模式。
常用的模式有以下几种:
"r":以只读模式打开文件。如果文件不存在,则打开失败。
"w":以写入模式打开文件。如果文件不存在,则创建文件。如果文件已经存在,则清空文件中原有的数据。
"a":以追加模式打开文件。如果文件不存在,则创建文件。如果文件已经存在,则将新数据添加到文件末尾。
"rb"、"wb"、"ab":以二进制模式打开文件。在 Windows 系统中,使用二进制模式打开文件时需要指定 "b" 模式。
fopen() 函数返回一个指向 FILE 结构体的指针。如果打开文件失败,则返回 NULL。在使用完文件后,需要使用 fclose() 函数关闭文件,以释放文件资源。
FILE *fp = fopen("myfile.txt", "r");
if (fp == NULL) {
// 处理文件打开失败的情况
}
fclose(fp);
读入链表
跟着思路来 打开了文件之后
将文件里的内容全部读入到链表当中 然后利用链表去进行一系列操作
void ReadFile(List *pList)
{
使用fopen函数打开文件text1.txt
以只读方式读取
返回一个指向FILE结构的指针fp
如果打开失败
则输出一条错误信息并退出程序
FILE* fp;
if ((fp = fopen("text1.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(0);
}
Node* p = NULL;
使用fgets函数读取一行数据
将数据存储在buf数组中
每次最多读取1024个字符
直到读到文件末尾为止
如果读取成功 则进入循环体
否则退出循环
char buf[1024]; // 用于存储每一行读入的数据
while (fgets(buf, 1024, fp) != NULL)
{
使用strtok函数将每一行数据解析成五个子串
分别对应姓名、性别、年龄、电话、地址
然后将这些子串存储到一个节点中
该节点的person字段为一个结构体
包含姓名、性别、年龄、电话、地址五个字段
p = new Node;
char* token = strtok(buf, " ");
p->person.m_Name = token;
token = strtok(NULL, " ");
p->person.m_Sex = atoi(token);
token = strtok(NULL, " ");
p->person.m_Age = atoi(token);
token = strtok(NULL, " ");
p->person.m_Phone = token;
token = strtok(NULL, "\n");
p->person.m_Addr = token;
if (pList->head == NULL)
{
pList->head = p;
pList->nail = p;
}
else
{
pList->nail->next = p;
pList->nail = p;
}
}
pList->nail->next = NULL;
}
从文件 "text1.txt" 中读取数据,并将其添加到链表 pList 中
打开文件 "text1.txt" 并使用 fgets() 函数逐行读取文件,每次读取一行数据并将其存储在缓冲区 buf 中
然后,函数将从缓冲区中提取每个字段,并将这些字段的值存储在一个新的节点 p 中,然后将这个节点添加到链表 pList 中。最后,函数将链表的尾指针 nail 指向最后一个节点。
需要注意的是,这个函数假设文件中的每一行数据都由五个字段组成,这些字段用空格分隔。然而,这个函数并没有检查文件格式是否正确,如果文件格式不正确,那么函数可能会读取到错误的数据,从而导致程序出现错误。因此,如果你想要使用这个函数来读取数据,需要确保文件的格式正确。同时,在读取完文件后,需要关闭文件以释放文件资源,可以使用 fclose() 函数来关闭文件
fgets()
从文件中读取一行数据
第一个参数 str 是一个字符数组,用于存储读取的数据。
第二个参数 n 是一个整数,表示要读取的数据的最大长度。
第三个参数 stream 是一个指向已打开的文件的指针。
fgets() 函数每次读取一行数据,并将其存储在 str 中。
如果读取成功,则返回 str 指向的字符串的地址。
如果已经到达文件末尾,则返回 NULL。
在代码中,fgets(buf, 1024, fp) 表示从已打开的文件 fp 中读取一行数据,并将其存储在 buf 中。如果读取成功,则继续执行循环体中的代码;如果已经到达文件末尾,则跳出循环。因此,fgets(buf, 1024, fp) != NULL 的意思是,只要还能从文件中读取一行数据,就继续执行循环体中的代码。
strtok()
将字符串分割成子字符串
str 是待分割的字符串,delim 是一个包含分割符的字符串。
在第一次调用时,strtok() 函数会将 str 中的第一个子字符串返回,并将 str 中的指针指向下一个子字符串的起始位置。在后续调用时,可以将 str 参数设置为 NULL,strtok() 函数会从上一次调用的位置继续分割字符串。
在代码中,strtok(buf, " ") 表示从 buf 中以空格为分隔符分割出第一个子字符串,并返回其指针。
第二个参数 " " 表示用空格作为分隔符,即将 buf 中的字符串按空格分割成若干个子字符串。
通过多次调用 strtok() 函数,可以将每一行数据分割成五个字段,并分别赋值给结构体 Person 的成员变量。
还能用其他符号:
char str[] = "Hello, world! How are you?";
char* token = strtok(str, " ,!");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, " ,!");
}
将字符串"Hello, world! How are you?"按照空格和逗号进行分解,并输出每个token
清空文件
在结束程序时 要把文件中原来的数据清空 然后写入这次执行完成后得到的数据
首先就得清空
//使用文件操作中的"w"模式打开文件,然后立即关闭文件,这样就会清空文件内容
void ClearFile(const char* filename)
{
FILE* fp = fopen(filename, "w");
if (fp == NULL)
{
printf("打开文件失败!\n");
exit(0);
}
fclose(fp);
}
原理前面讲过 不再重复
写入文件
void WriteFile(List* pList)
{
FILE* fp;
if ((fp = fopen("text1.txt", "w")) == NULL)
{
cout << "文件打开失败!" << endl;
exit(0);
}
Node* p = pList->head->next;
while (p)
{
fprintf(fp, "%s %d %d %s %s\n", p->person.m_Name.c_str(), p->person.m_Sex, p->person.m_Age, p->person.m_Phone.c_str(), p->person.m_Addr.c_str());
p = p->next;
}
fclose(fp);
}
将链表中的每个节点的信息按照指定格式写入文件中。
使用fopen()函数打开文件并以写的方式打开
使用fprintf()函数将每个节点中的信息格式化写入文件中,最后关闭文件。
在while循环中
首先使用fprintf()函数将节点中的姓名、性别、年龄、电话、地址按照指定格式写入文件中,使用c_str()函数将string类型转换为C风格字符串
将指针p指向下一个节点,继续写入文件。
最后,使用fclose()函数关闭文件。
c_str()
C++标准库中的一个函数,可以将string类型的字符串转换为C风格的字符串(即以'\0'结尾的char数组),用于与C语言库函数交互。
返回const char*指针,指向string对象中的字符数组,注意是const指针,因此不能修改c_str()返回的C字符串中的字符,否则会导致未定义的行为。
代码
整体逻辑和实现分析完毕 以下是完整代码
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<string>
#include<easyx.h>
#include<cstdlib>//解决system不明确问题
using namespace std;
struct Person
{
string m_Name;
int m_Sex;
int m_Age;
string m_Phone;
string m_Addr;
};
typedef struct addressbooks
{
Person person;
struct addressbooks* next;
}Node;
typedef struct _list
{
Node* head;
Node* nail;
}List;
void button(int x, int y, int w, int h, TCHAR* text)
{
setbkmode(TRANSPARENT);
setfillcolor(BROWN);
fillroundrect(x, y, x + w, y + h, 10, 10);
settextstyle(30, 0, "黑体");
TCHAR text_[100];
for (int i = 0; i < 100; i++)
text_[i] = text[i];
int tx = x + (w - textwidth(text)) / 2;
int ty = y + (h - textheight(text)) / 2;
outtextxy(tx, ty, text);
}
void initMenu()
{
initgraph(300, 600, EX_SHOWCONSOLE);
setbkcolor(RGB(77, 95, 107));
cleardevice();
IMAGE image;
loadimage(&image,"星之卡比.jpg");
putimage(0, 0, &image);
TCHAR A[] = "显示联系人";
TCHAR B[] = "查询联系人";
TCHAR C[] = "添加联系人";
TCHAR D[] = "关闭通讯录";
button(50, 125, 200, 50, A);
button(50, 225, 200, 50, B);
button(50, 325, 200, 50, C);
button(50, 425, 200, 50, D);
}
void InitList(List* pList)
{
Node* L = new Node;
pList->head = L;
pList->nail = pList->head;
pList->nail->next = NULL;
}
void addPerson(List* plist)
{
Node* p = new Node;
string name;
cout << "请输入姓名:" << endl;
cin >> name;
p->person.m_Name = name;
cout << "请输入性别:(1男2女)" << endl;
int sex = 0;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2)
{
p->person.m_Sex = sex;
break;
}
cout << "输入有误,请重新输入" << endl;
}
cout << "请输入年龄:" << endl;
int age = 0;
while (true)
{
cin >> age;
if (age >= 0 && age <= 100)
{
p->person.m_Age = age;
break;
}
cout << "输入有误,请重新输入" << endl;
}
cout << "请输入联系电话:" << endl;
string phone;
cin >> phone;
p->person.m_Phone = phone;
cout << "请输入家庭住址:" << endl;
string address;
cin >> address;
p->person.m_Addr = address;
p->next = NULL;
plist->nail->next = p;
plist->nail = p;
cout << "添加成功" << endl;
system("pause");
system("cls");
}
void showPerson(List* pList)
{
if (pList->head->next == 0)
{
cout << "当前记录为空" << endl;
}
else
{
Node* p;
for (p = pList->head->next; p; p = p->next)
{
cout << "姓名:" << p->person.m_Name << "\t\t";
cout << "性别:" << (p->person.m_Sex == 1 ? "男" : "女") << "\t\t";
cout << "年龄:" << p->person.m_Age << "\t\t";
cout << "电话:" << p->person.m_Phone << "\t\t";
cout << "住址:" << p->person.m_Addr << endl;
}
}
system("pause");
system("cls");
}
Node* isExist(List* plist, string name)
{
Node* p;
p = plist->head->next;
while (p && p->person.m_Name != name)
{
p = p->next;
}
if (p == NULL)
{
cout << "查无此人" << endl;
}
return p;
}
void show(Node* p)
{
cout << "姓名:" << p->person.m_Name << "\t\t";
cout << "性别:" << (p->person.m_Sex == 1 ? "男" : "女") << "\t\t";
cout << "年龄:" << p->person.m_Age << "\t\t";
cout << "电话:" << p->person.m_Phone << "\t\t";
cout << "住址:" << p->person.m_Addr << endl;
}
void showmodify()
{
cout << "*********************" << endl;
cout << "*****(1)修改姓名*****" << endl;
cout << "*****(2)修改性别*****" << endl;
cout << "*****(3)修改年龄*****" << endl;
cout << "*****(4)修改电话*****" << endl;
cout << "*****(5)修改住址*****" << endl;
cout << "*********************" << endl;
}
void modifyPerson(List* plist,string name)
{
Node* p, * q, * x;
x = isExist(plist, name);
if (x)
{
for (q = plist->head->next; q; q = q->next)
{
if (q->person.m_Name == name)
{
show(q);
}
}
showmodify();
int button = 0;
cin >> button;
for (p = plist->head->next; p; p = p->next)
{
if (p == x)
{
switch (button)
{
case 1:
{
string name;
cout << "请输入姓名:" << endl;
cin >> name;
p->person.m_Name = name;
break;
}
case 2:
{
cout << "请输入性别:(1男2女)" << endl;
int sex = 0;
while (true)
{
cin >> sex;
if (sex == 1 || sex == 2)
{
p->person.m_Sex = sex;
break;
}
cout << "输入有误,请重新输入" << endl;
}
break;
}
case 3:
{
cout << "请输入年龄:" << endl;
int age = 0;
while (true)
{
cin >> age;
if (age >= 0 && age <= 100)
{
p->person.m_Age = age;
break;
}
cout << "输入有误,请重新输入" << endl;
}
break;
}
case 4:
{
cout << "请输入联系电话" << endl;
string phone;
cin >> phone;
p->person.m_Phone = phone;
break;
}
case 5:
{
cout << "请输入家庭住址" << endl;
string address;
cin >> address;
p->person.m_Addr = address;
break;
}
}
}
}
cout << "修改成功!" << endl;
}
system("pause");
system("cls");
}
void deletePerson(List* plist,string name)
{
Node* p, * q, * x;
x = isExist(plist, name);
if (x)
{
for (q = plist->head, p = plist->head->next; p; q = p, p = p->next)
{
if (p->person.m_Name == name)
{
q->next = p->next;
delete(p);
cout << "删除成功" << endl;
break;
}
}
}
system("pause");
system("cls");
}
void UDMenu()
{
cleardevice();
setbkcolor(RGB(77, 95, 107));
cleardevice();
IMAGE image;
loadimage(&image, "卡比2.jpg");
putimage(0, 0, &image);
TCHAR A[] = "修改联系人";
TCHAR B[] = "删除联系人";
TCHAR C[] = "确定";
button(50, 175, 200, 50, A);
button(50, 275, 200, 50, B);
button(50, 375, 200, 50, C);
}
void operate(List* plist,string name)
{
ExMessage msg;
while (true)
{
if (peekmessage(&msg, EX_MOUSE))
{
switch (msg.message)
{
case WM_LBUTTONDOWN:
{
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 175 && msg.y <= 225)
{//修改联系人
modifyPerson(plist, name);
break;
}
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 275 && msg.y <= 325)
{//删除联系人
deletePerson(plist, name);
break;
}
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 375 && msg.y <= 425)
{
initMenu();
return;
}
}
}
}
}
}
void findPerson(List* plist)
{
cout << "请输入要查找的联系人:" << endl;
string name;
cin >> name;
Node* p, * x;
x = isExist(plist, name);
if (x)
{
for (p = plist->head->next; p; p = p->next)
{
if (p->person.m_Name == name)
{
show(p);
cout << "是否要进行修改、删除操作?" << endl;
UDMenu();
operate(plist,name);
break;
}
}
}
system("pause");
system("cls");
}
void certain()
{
cout << "是否要清空通讯录记录?" << endl;
cout << "***************" << endl;
cout << "****(0)取消****" << endl;
cout << "****(1)确定****" << endl;
cout << "***************" << endl;
}
void CreateFile()
{
FILE* fp = NULL;
if ((fp = fopen("text1.txt", "w")) == NULL) {
printf("无法创建文件!\n");
exit(1);
}
else {
printf("文件创建成功!\n");
}
fclose(fp);
}
void ReadFile(List *pList)
{
FILE* fp;
if ((fp = fopen("text1.txt", "r")) == NULL)
{
printf("打开文件失败!\n");
exit(0);
}
Node* p = NULL;
char buf[1024]; // 用于存储每一行读入的数据
while (fgets(buf, 1024, fp) != NULL)
{
p = new Node;
char* token = strtok(buf, " ");
p->person.m_Name = token;
token = strtok(NULL, " ");
p->person.m_Sex = atoi(token);
token = strtok(NULL, " ");
p->person.m_Age = atoi(token);
token = strtok(NULL, " ");
p->person.m_Phone = token;
token = strtok(NULL, "\n");
p->person.m_Addr = token;
if (pList->head == NULL)
{
pList->head = p;
pList->nail = p;
}
else
{
pList->nail->next = p;
pList->nail = p;
}
}
pList->nail->next = NULL;
}
void WriteFile(List* pList)
{
FILE* fp;
if ((fp = fopen("text1.txt", "w")) == NULL)
{
cout << "文件打开失败!" << endl;
exit(0);
}
Node* p = pList->head->next;
while (p)
{
fprintf(fp, "%s %d %d %s %s\n", p->person.m_Name.c_str(), p->person.m_Sex, p->person.m_Age, p->person.m_Phone.c_str(), p->person.m_Addr.c_str());
p = p->next;
}
fclose(fp);
}
//使用文件操作中的"w"模式打开文件,然后立即关闭文件,这样就会清空文件内容
void ClearFile(const char* filename)
{
FILE* fp = fopen(filename, "w");
if (fp == NULL)
{
printf("打开文件失败!\n");
exit(0);
}
fclose(fp);
}
void Close(List* plist)
{
certain();
int button = 0;
cin >> button;
if (button == 1)
{
Node* p, * q;
p = plist->head->next;
while (p)
{
q = p->next;
delete(p);
p = q;
}
plist->head->next = NULL;
plist->nail = plist->head;
ClearFile("text1.txt");
cout << "通讯录已清空,成功退出" << endl;
}
else if (button == 0)
{
ClearFile("text1.txt");
WriteFile(plist);
cout << "成功退出" << endl;
}
system("pause");
system("cls");
}
int main()
{
List abs;
InitList(&abs);
initMenu();
FILE* fp;
// 打开文件
if ((fp = fopen("text1.txt", "r")) == NULL)
{
CreateFile(); // 如果文件不存在,则创建文件
fp = fopen("text1.txt", "r"); // 重新打开文件
if (fp == NULL)
{
printf("无法打开文件!\n");
exit(1);
}
}
//读入文件
ReadFile(&abs);
ExMessage msg;
while (true)
{
if (peekmessage(&msg, EX_MOUSE))
{
switch (msg.message)
{
case WM_LBUTTONDOWN:
{
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 125 && msg.y <= 175)
{//显示联系人
showPerson(&abs);
break;
}
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 225 && msg.y <= 275)
{//查询联系人(修改?删除?)
findPerson(&abs);
break;
}
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 325 && msg.y <= 375)
{
addPerson(&abs);
break;
}
if (msg.x >= 50 && msg.x <= 250 && msg.y >= 425 && msg.y <= 475)
{
Close(&abs);
goto END;
}
}
}
}
}
END:
cout << "欢迎下次使用" << endl;
getchar();
closegraph();
return 0;
}
注意 图片需要自行添加到同级目录
如果在本地电脑的vs 就添加到这个项目文件夹里面
(txt文件也会在这里)
如果是打包发人 就需要放在exe同级文件中
附件
📄 c与文件读写.docx(原 word 笔记):c与文件读写.docx
⬅️ C语言 🏠 00-编程语言 ➡️ C语言与MySQL连接
💬 评论