c与文件读写

对于通讯录系统进行一个改进 加上文件读写功能 成为真正意义上的通讯录

两种思路

增删改查分别对文件内容进行改写 代码工程量可能很大 但是程序执行起来需要改写的地方会少一些 对于数据量庞大的项目可以采用这种 但是一般都用连接数据库了 之前试过的连接mysql已经可以实现这种操作 就不再多此一举了 本来文件读写就是了解性的东西 实际用不太到了

然后一种思路就是后面摸索出来发现前面做了很多无用功 其实对于这种通讯录啊什么的 这种小数据的东西 直接在程序执行前 将文件里的内容全部读入到链表当中 然后再在链表中完成所有操作 最后将文件原有内容清空 用处理后的数据覆盖上去就OK了

对于之前的程序基本没什么改变 就是添加了一个读模块和写模块

接下来分析实现

准备

#define _CRT_SECURE_NO_WARNINGS

防止c语法中的文件读写函数报错

打开

首先在主函数中

cfile-image1
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()

cfile-image2

第一个参数 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);

读入链表

cfile-image3

跟着思路来 打开了文件之后

将文件里的内容全部读入到链表当中 然后利用链表去进行一系列操作

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()

从文件中读取一行数据

cfile-image4

第一个参数 str 是一个字符数组,用于存储读取的数据。

第二个参数 n 是一个整数,表示要读取的数据的最大长度。

第三个参数 stream 是一个指向已打开的文件的指针。

fgets() 函数每次读取一行数据,并将其存储在 str 中。

如果读取成功,则返回 str 指向的字符串的地址。

如果已经到达文件末尾,则返回 NULL。

cfile-image5

在代码中,fgets(buf, 1024, fp) 表示从已打开的文件 fp 中读取一行数据,并将其存储在 buf 中。如果读取成功,则继续执行循环体中的代码;如果已经到达文件末尾,则跳出循环。因此,fgets(buf, 1024, fp) != NULL 的意思是,只要还能从文件中读取一行数据,就继续执行循环体中的代码。

strtok()

将字符串分割成子字符串

cfile-image6

str 是待分割的字符串,delim 是一个包含分割符的字符串。

在第一次调用时,strtok() 函数会将 str 中的第一个子字符串返回,并将 str 中的指针指向下一个子字符串的起始位置。在后续调用时,可以将 str 参数设置为 NULL,strtok() 函数会从上一次调用的位置继续分割字符串。

在代码中,strtok(buf, " ") 表示从 buf 中以空格为分隔符分割出第一个子字符串,并返回其指针。

第二个参数 " " 表示用空格作为分隔符,即将 buf 中的字符串按空格分割成若干个子字符串。

通过多次调用 strtok() 函数,可以将每一行数据分割成五个字段,并分别赋值给结构体 Person 的成员变量。

还能用其他符号:

char str[] = "Hello, world! How are you?";
cfile-image7
char* token = strtok(str, " ,!");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, " ,!");
}

将字符串"Hello, world! How are you?"按照空格和逗号进行分解,并输出每个token

清空文件

cfile-image8

在结束程序时 要把文件中原来的数据清空 然后写入这次执行完成后得到的数据

首先就得清空

//使用文件操作中的"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 就添加到这个项目文件夹里面

cfile-image9

(txt文件也会在这里)

如果是打包发人 就需要放在exe同级文件中

cfile-image10

附件

📄 c与文件读写.docx(原 word 笔记):c与文件读写.docx


⬅️ C语言 🏠 00-编程语言 ➡️ C语言与MySQL连接