我刚刚做了一个需要使用到文件操作和链表的小项目,也踩了不少坑,现在对文件操作也有了新的认知,现在分享给大家。
#fopen_s函数的简介
##函数原型
errno_t fopen_s(FILE** pFile, const char* filename, const char* mode);
##返回值
当打开失败时,返回errno_t类型的错误代码(该错误代码是可以被解析的)
当打开成功时,返回值是0,pFile变为一个有效内存地址(相当于是文件的一个代理指针),以后再对文件中的内容进行操作的时候就用这个指针。
##参数
其中,Pfile就是FILE*类型的变量的指针。
filename就是文件名。
mode就是打开方式,以下是常用的打开方式:
| 模式 | 描述 | 文件存在 | 文件不存在 | 文件位置 |
|---|---|---|---|---|
"r" | 只读 | 打开成功 | 打开失败 | 文件开头 |
"w" | 只写 | 清空内容 | 创建新文件 | 文件开头 |
"a" | 追加 | 打开成功 | 创建新文件 | 文件末尾 |
"r+" | 读写 | 打开成功 | 打开失败 | 文件开头 |
"w+" | 读写 | 清空内容 | 创建新文件 | 文件开头 |
"a+" | 读写 | 打开成功 | 创建新文件 | 文件末尾 |
##使用
1、数据读取
发现了没有,当我们想在不改变文件中的数据的情况下将文件中的数据写入链表时,采用“r”的方式是最好的,我现在写一个简单的打开函数:
FILE* file=NULL;
errno_t flag = 0;
flag = fopen_s(&file, DATA_FILE, "r");
//只读
//fopen_s成功返回0,失败返回错误代码
if (flag != 0)
{
printf("打开储存文件失败\n");
return;
}
//读取操作......
此函数是用来读取的,当我们需要逐行读取时,我们可以这样:
char line[200];
//作为一个中间值
while(fgets(line,sizeof(line),file)!=NULL)
{
将数据存入链表......
}
fgets函数在读取到\n后会截止并自动跳到下一行,当fgets函数没读到数据时会返回值NULL,
通过这种方式我们就可以实现每一行中间值的读取。
整个的读取代码:
int num = 0;
char name[20];
int score = 0;
int count = 0;
int succes_num = 0;
int file_num = 0;
char line[100];
while (fgets(line,sizeof(line),file)!=NULL)
{
if (sscanf_s(line, "学号是%d 姓名是%s 成绩是%d\n", &num, name,(unsigned)sizeof(name), &score) == 3)
{
struct stu_inf* p = (struct stu_inf*)malloc(sizeof(struct stu_inf));
p->num = num;
strcpy_s(p->name, sizeof(p->name), name);
p->score = score;
printf("学号是%d 姓名是%s 成绩是%d的数据读取成功\n", p->num, p->name, p->score);
succes_num++;
p->next = head;
head = p;
}
else
{
printf("第%d个数据读取失败\n",count);
file_num++;
}
count++;
}
if (sscanf_s(line, "学号是%d 姓名是%s 成绩是%d\n", &num, name,(unsigned)sizeof(name), &score) == 3)
这里我先读取中间值,再判断读取是否成功,成功了才分配内存。
struct stu_inf* p = (struct stu_inf*)malloc(sizeof(struct stu_inf));
这里为什么要进行内存分配呢?
-
不知道会有多少学生
-
每个学生的大小相同但数量不确定
-
需要链表结构动态增长
然后我们将每一个分配了内存的节点连接起来,就实现了数据的读取。
2、数据的更新
那么,我们在将数据读取的数据进行修改后,再将修改同步到文件应该如何做呢?
此时问题就来了,我在编写的时候,因为不熟悉fopen_s的写入方式,在判定了链表中的头指针head==NULL后便直接return,这导致我无法将文件中的数据删干净。后来,我查到了问题的来源,加了一个只是使用”w“打开的操作,问题就迎刃而解了。代码如下:
if (head == NULL)
{
errno_t result = fopen_s(file, DATA_FILE, "w");
printf("文件已清空\n");
fclose(file);
return;
}
当经过操作后,链表中的数据仍然有剩余的时候,我们使用fprintf将数据写入文件:
if (fprintf(file, "学号是%d 姓名是%s 成绩是%d\n", p->num, p->name, p->score) >= 0)
{
if (flag == 1)
{
printf("学号是%d,姓名是%s,成绩是%d的数据储存成功\n", p->num, p->name, p->score);
}
succes_num++;
}
else
{
if (flag == 1)
{
printf("学号是%d,姓名是%s,成绩是%d的数据储存失败\n", p->num, p->name, p->score);
}
file_num++;
}
fprintf的返回值:
-
成功: 返回写入的字符数(包括换行符等)
-
失败: 返回负数
这里和sscanf_s的返回值是很像的,只不过sscanf_s返回的是成功读取的数据个数,fprintf返回的是成功写入的字符数罢了。
具体代码:
void save_to_file(int flag)
{
if (DATA_FILE == NULL)
{
printf("文件不存在\n");
}
FILE* file = NULL;
//FILE类型本来就是指针
if (fopen_s(&file, DATA_FILE, "w") != 0)
{
printf("打开文件失败,无法储存\n");
return;
}
if (head == NULL)
{
errno_t result = fopen_s(file, DATA_FILE, "w");
printf("文件已清空\n");
fclose(file);
return;
}
//fuck GUET
else
{
struct stu_inf* to_delete = NULL;
int succes_num = 0;
int file_num = 0;
struct stu_inf* p = head;
while (p != NULL)
{
if (fprintf(file, "学号是%d 姓名是%s 成绩是%d\n", p->num, p->name, p->score) >= 0)
{
if (flag == 1)
{
printf("学号是%d,姓名是%s,成绩是%d的数据储存成功\n", p->num, p->name, p->score);
}
succes_num++;
}
else
{
if (flag == 1)
{
printf("学号是%d,姓名是%s,成绩是%d的数据储存失败\n", p->num, p->name, p->score);
}
file_num++;
}
to_delete = p;
free(to_delete);
p = p->next;
}
int total = succes_num + file_num;
if (flag == 1)
{
printf("在%d人中有%d个储存成功、%d个储存失败\n", total, succes_num, file_num);
}
fclose(file);
//free(file);此处不应该free,因为free是释放内存,而且之前就已经fclose过了
}
}
fopen_s函数使用详解

被折叠的 条评论
为什么被折叠?



