2024-2025-1 20241314 《计算机基础与程序设计》第十三周学习总结
2024-2025-1 20241314 《计算机基础与程序设计》第十三周学习总结
作业信息
这个作业属于哪个课程 | 2024-2025-1-计算机基础与程序设计 |
---|---|
这个作业要求在哪里 | 2024-2025-1计算机基础与程序设计第十三周作业 |
这个作业的目标 | |
作业正文 | 正文 |
教材学习内容总结
第12章:文件操作
1. 文件的基本概念
- 文件的定义:文件是存储在计算机磁盘上的一组数据。文件可以是文本文件(如
.txt
、.c
、.h
)或二进制文件(如图像、音频等)。 - 文件系统:操作系统对于文件的管理称为文件系统,包括文件的创建、读取、写入和删除操作。有时候文件会以流的形式被访问。
2. 文件操作的基本函数
-
打开文件
- 使用
fopen()
函数打开文件:FILE *fopen(const char *filename, const char *mode);
filename
是要打开的文件名。mode
表示打开文件的模式:"r"
:只读模式。"w"
:写入模式(会清空文件内容或创建新文件)。"a"
:附加模式(在文件末尾写入)。"rb"
、"wb"
、"ab"
:读取、写入和附加二进制文件。
- 返回值:成功时返回文件指针,失败时返回
NULL
。
- 使用
-
关闭文件
- 使用
fclose()
函数关闭文件:int fclose(FILE *stream);
- 传入文件指针,成功返回
0
,失败返回 EOF。
- 传入文件指针,成功返回
- 使用
-
读取文件
fgetc()
:读取一个字符。int fgetc(FILE *stream);
fgets()
:读取一行字符,直到换行符或文件结束。char *fgets(char *str, int num, FILE *stream);
fscanf()
:格式化读取数据。int fscanf(FILE *stream, const char *format, ...);
-
写入文件
fputc()
:写入一个字符。int fputc(int char, FILE *stream);
fputs()
:写入一行字符串。int fputs(const char *str, FILE *stream);
fprintf()
:格式化写入数据。int fprintf(FILE *stream, const char *format, ...);
3. 文件指针的使用
- FILE 结构体:
FILE
是 C 标准库定义的一个类型,用于描述打开的文件。 - 文件指针:通过指针对文件进行操作,例如:
FILE *file = fopen("example.txt", "r");
- 获取文件位置:
ftell()
:获取当前文件指针的位置。long ftell(FILE *stream);
fseek()
:设置文件指针的位置。int fseek(FILE *stream, long offset, int whence);
whence
可以是:SEEK_SET
:文件开头。SEEK_CUR
:当前指针位置。SEEK_END
:文件末尾。
4. 错误处理
- 文件操作中的错误:
- 可以使用全局变量
errno
来获取错误信息。 perror()
函数用于输出错误描述:void perror(const char *str);
- 可以使用全局变量
5. 示例代码
下面是一个简单的文件读取和写入示例:
写入文件的示例:
#include <stdio.h>
int main() {
FILE *fp = fopen("output.txt", "w");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
fprintf(fp, "Hello, World!\n");
fclose(fp);
return 0;
}
读取文件的示例:
#include <stdio.h>
int main() {
FILE *fp = fopen("output.txt", "r");
if (fp == NULL) {
perror("Error opening file");
return -1;
}
char buffer[100];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("%s", buffer);
}
fclose(fp);
return 0;
}
6. 其他高级特性
- 缓冲区管理:C 的文件流是使用缓冲区进行管理的,这提高了 I/O 操作的效率。
- 随机访问:通过
fseek()
和ftell()
函数,实现文件的随机读写。
教材学习中的问题和解决过程
- 问 程序中写入文件出现乱码
- 答
-
问题分析
-
-
文件打开模式问题
在case 6
(写入文件操作)中,使用"w"
模式打开文件。如果文件已经存在,"w"
模式会先清空文件原有内容再写入新内容。而在case 7
(读取文件操作)中,直接按顺序从文件读取数据期望得到之前写入的内容,但没有考虑到这种清空情况可能带来的不一致等问题,不过这不是导致乱码的直接原因。 -
数据写入和读取的二进制与文本格式问题
程序中使用fwrite
和fread
进行结构体数据的读写操作,这两个函数是以二进制形式进行数据的输入输出的。而直接用文本编辑器去打开以二进制形式写入的文件,文本编辑器会按照其默认的文本编码方式(比如常见的ASCII、UTF-8等)去解析二进制数据,就会出现乱码情况。因为结构体中的数据比如long
型、int
型等在内存中的二进制存储格式和文本表示格式(文本编辑器期望的格式)是完全不同的,文本编辑器不能正确解析这些二进制数据并展示成可读的文本形式。-
解决办法
-
-
修改文件打开模式(可选优化)
如果希望保留文件之前的内容,后续写入新内容时追加到文件末尾,可以把case 6
中文件打开模式"w"
修改为"a"
(追加模式)。这样在多次运行写入操作时,之前的数据不会丢失。不过这和乱码问题本身关联性不大,只是一种更好的文件处理方式上的优化。 -
采用文本格式进行读写(关键解决办法)
- 写入文件时转换为文本格式:可以修改
case 6
中的写入逻辑,将结构体中的每个成员按照文本格式依次写入文件,例如:
- 写入文件时转换为文本格式:可以修改
if((fp = fopen("student.txt", "w")) == NULL) {
printf("failure to open student.txt!");
exit(0);
}
for (int i = 0; i < n; i++) {
fprintf(fp, "%ld %s %d %d %d %d %.1f\n", stu[i].id, stu[i].name,
stu[i].score[0], stu[i].score[1], stu[i].score[2],
stu[i].total, stu[i].aver);
}
fclose(fp);
上述代码通过fprintf
函数,按照一定的文本格式(用空格等分隔不同成员数据,并换行区分不同的学生记录)将结构体数据转换为文本形式写入文件。
- 读取文件时解析文本格式:相应地,修改case 7
中的读取逻辑,使用fscanf
函数从文件按文本格式解析数据到结构体中,例如:
if((fp = fopen("student.txt", "r")) == NULL) {
printf("failure to open student.txt!");
exit(0);
}
int i = 0;
while (fscanf(fp, "%ld %s %d %d %d %d %f", &stu[i].id, stu[i].name,
&stu[i].score[0], &stu[i].score[1], &stu[i].score[2],
&stu[i].total, &stu[i].aver) == 7) {
i++;
}
fn = i;
fclose(fp);
这里利用fscanf
按照之前写入的文本格式去解析文件中的数据,并存入结构体数组中,从而可以正确地读写文本格式的学生信息文件,避免出现乱码情况,并且能实现数据的持久化存储和后续读取复用等功能。
通过上述对文件读写方式的修改,将二进制读写改为文本格式的读写,就能解决用文本编辑器打开文件出现乱码的问题,同时也能合理地实现学生信息数据的存储和读取操作。
- 问 如何使用指针交换结构体数组里元素的顺序
- 答
- 指针交换的基本原理
- 要交换结构体数组中的元素顺序,可以通过交换指向结构体的指针所指向的内容来实现。在C语言中,结构体变量在内存中有自己的存储空间,而指针可以指向这些结构体变量。通过操作指针,可以方便地交换结构体变量的位置。
- 具体实现步骤
- 以下是一个简单的示例代码,用于交换结构体数组中两个元素的位置:
#include <stdio.h>
#include <string.h>
// 定义结构体
typedef struct student
{
long id;
char name[10];
int score[3];
int total;
float aver;
} STUDENT;
// 交换函数
void swap(STUDENT *a, STUDENT *b)
{
STUDENT temp;
// 将a指向的结构体内容复制到temp
temp = *a;
// 将b指向的结构体内容复制到a指向的结构体
*a = *b;
// 将temp中的内容(原来a指向的结构体内容)复制到b指向的结构体
*b = temp;
}
- 在上述代码中,
swap
函数接受两个指向STUDENT
结构体的指针a
和b
。首先,创建一个临时的STUDENT
结构体变量temp
,然后将*a
(a
所指向的结构体)的内容复制到temp
中。接着,将*b
(b
所指向的结构体)的内容复制到*a
中,最后将temp
中的内容(原来*a
的内容)复制到*b
中,从而实现了两个结构体内容的交换。
- 在排序算法中的应用(以冒泡排序为例)
- 假设要对一个
STUDENT
结构体数组stu
进行排序,可以使用冒泡排序算法结合上述的交换函数来实现。以下是一个简单的示例代码:
- 假设要对一个
void bubbleSort(STUDENT stu[], int n)
{
int i, j;
for (i = 0; i < n - 1; i++)
{
for (j = 0; j < n - i - 1; j++)
{
// 根据某个条件比较两个结构体元素,这里假设按照total成员排序
if (stu[j].total > stu[j + 1].total)
{
swap(&stu[j], &stu[j + 1]);
}
}
}
}
- 在这个冒泡排序函数
bubbleSort
中,通过两层循环遍历结构体数组stu
。在内层循环中,比较相邻的两个结构体元素(通过stu[j].total
和stu[j + 1].total
比较,这里假设按照total
成员进行排序)。如果满足交换条件(这里是前一个元素的total
大于后一个元素的total
),就调用swap
函数交换这两个结构体元素的位置。经过多次循环,数组中的结构体元素就会按照total
成员的升序(在这个例子中)排列。
基于AI的学习