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() 函数,实现文件的随机读写。

教材学习中的问题和解决过程

- 程序中写入文件出现乱码

    • 问题分析

  1. 文件打开模式问题
    case 6(写入文件操作)中,使用"w"模式打开文件。如果文件已经存在,"w"模式会先清空文件原有内容再写入新内容。而在case 7(读取文件操作)中,直接按顺序从文件读取数据期望得到之前写入的内容,但没有考虑到这种清空情况可能带来的不一致等问题,不过这不是导致乱码的直接原因。

  2. 数据写入和读取的二进制与文本格式问题
    程序中使用fwritefread进行结构体数据的读写操作,这两个函数是以二进制形式进行数据的输入输出的。而直接用文本编辑器去打开以二进制形式写入的文件,文本编辑器会按照其默认的文本编码方式(比如常见的ASCII、UTF-8等)去解析二进制数据,就会出现乱码情况。因为结构体中的数据比如long型、int型等在内存中的二进制存储格式和文本表示格式(文本编辑器期望的格式)是完全不同的,文本编辑器不能正确解析这些二进制数据并展示成可读的文本形式。

    • 解决办法

  3. 修改文件打开模式(可选优化)
    如果希望保留文件之前的内容,后续写入新内容时追加到文件末尾,可以把case 6中文件打开模式"w"修改为"a"(追加模式)。这样在多次运行写入操作时,之前的数据不会丢失。不过这和乱码问题本身关联性不大,只是一种更好的文件处理方式上的优化。

  4. 采用文本格式进行读写(关键解决办法)

    • 写入文件时转换为文本格式:可以修改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按照之前写入的文本格式去解析文件中的数据,并存入结构体数组中,从而可以正确地读写文本格式的学生信息文件,避免出现乱码情况,并且能实现数据的持久化存储和后续读取复用等功能。

通过上述对文件读写方式的修改,将二进制读写改为文本格式的读写,就能解决用文本编辑器打开文件出现乱码的问题,同时也能合理地实现学生信息数据的存储和读取操作。

- 如何使用指针交换结构体数组里元素的顺序

  1. 指针交换的基本原理
    • 要交换结构体数组中的元素顺序,可以通过交换指向结构体的指针所指向的内容来实现。在C语言中,结构体变量在内存中有自己的存储空间,而指针可以指向这些结构体变量。通过操作指针,可以方便地交换结构体变量的位置。
  2. 具体实现步骤
    • 以下是一个简单的示例代码,用于交换结构体数组中两个元素的位置:
#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结构体的指针ab。首先,创建一个临时的STUDENT结构体变量temp,然后将*aa所指向的结构体)的内容复制到temp中。接着,将*bb所指向的结构体)的内容复制到*a中,最后将temp中的内容(原来*a的内容)复制到*b中,从而实现了两个结构体内容的交换。
  1. 在排序算法中的应用(以冒泡排序为例)
    • 假设要对一个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].totalstu[j + 1].total比较,这里假设按照total成员进行排序)。如果满足交换条件(这里是前一个元素的total大于后一个元素的total),就调用swap函数交换这两个结构体元素的位置。经过多次循环,数组中的结构体元素就会按照total成员的升序(在这个例子中)排列。

基于AI的学习




posted @ 2024-12-22 12:36  欧阳嘉盛  阅读(0)  评论(0编辑  收藏  举报