【C复习】04:文件操作

文件操作


所谓文件一般都是指存储在外部介质上数据的集合,输入输出时数据传输的过程,即数据流。

根据数据的组织形式,数据文件可以分为ASCII文件和二进制文件,

  • 数据在内存中是以二进制存储的,如果不加转换的输出到外存,可以认为其就是存储在内存数据中的映像,称之为映像文件
  • 如果要求在外存上以ACSII代码形式存储,则需要再存储前进行转换,称之为本文文件

数据在磁盘上的存储方式:字符文件一律以ASCII形式存储,数值型数据既可以用ASCII形式存储,也可以用二进制形式存储。

每个被使用的文件都在内存中开辟一个相应的文件信息区,用来存放文件的相关信息(文件的名字、文件状态以及文件当前位置),这些信息是保存在一个结构体变量中的,FILE:

typedef struct {
    short level;//缓冲区满or空
    unsigned flags;//文件状态标志
    char fd;//文件描述符
    unsigned char hold;//如缓冲区无内容不读取字符
    short bsize;//缓冲区的大小
    unsigned char *buffer;//数据缓冲区的位置
    unsigned char *curp;//指针当前的指向
    unsigned istemp;//临时文件指示器
    short token;//用于有效性检查
} FILE;

每一个FILE类型变量对应一个文件的信息区,将指向文件信息区的指针称为文件指针。

注:指向文件的指针变量并不是指向外部介质上的数据文件的开头,而是指向内存中的文件信息区的开头

一、打开与关闭文件

1.fopen

所谓打开文件是指为文件建立相应的信息区(存放有关文件信息)与文件缓冲区(暂时存放输入输出数据)。

//常用的打开文件的方式
FILE *fp;
if ((fp = fopen("file.txt", "r")) == NULL) {
    printf("can not open this file\n");
    exit(0);
}

image-20230317211703764

程序中可以使用3个标准的流文件:标准输入stdin、标准输出stdout、标准错误输出stderr

标准输入流是从终端的输入、标准输出流是向终端的输出、标准错误输出流是程序出错将错误信息发送到终端。

程序开始运行时系统自动打开这3个标准流文件,程序编写者直接使用即可。

2.fclose

关闭文件就是撤销文件信息区与文件缓冲区,使文件指着不再指向该文件。

如果不关闭文件将会丢失数据,因为向文件写入数据时是先将数据输入到缓冲区,待缓冲区满之后才会写入磁盘(缓冲区数据丢失)。

fclose(fp);

二、顺序读写数据文件

在顺序读写中,先写入的数据存放在文件中前面的位置,后写入的数据存放在文件中后面的位置。

1.向文件读写字符

image-20230317215643717

  • 用fgetc函数从键盘逐个读入字符 然后用fputc函数将数据写入磁盘文件:
#include<stdio.h>
#include<stdlib.h>

int main() {
    FILE *fp;
    char ch;
    char filename[10];
    printf("please enter a filename:");
    scanf("%s", filename);
    if ((fp = fopen(filename, "w")) == NULL) {
        printf("can not open this file\n");
        exit(0);
    }
    ch = getchar();//用于接收最后输入的回车符
    printf("please enter a char array end with a '#':");
    ch = getchar();
    while (ch != '#') {
        fputc(ch, fp);//向磁盘文件写入一个字符
        ch = getchar();
    }
    fclose(fp);
    return 0;
}

成功通过fputc函数向test.txt文件中逐个字符写入,文件操作成功。

  • 将一个磁盘文件中的信息复制到另一个磁盘文件中:
#include<stdio.h>
#include<stdlib.h>

int main() {
    FILE *inputfp, *outputfp;
    char ch;
    char srcFileName[20], destFileName[20];
    printf("enter the input file name:");
    scanf("%s", srcFileName);
    printf("enter the output file name:");
    scanf("%s", destFileName);
    if ((inputfp = fopen(srcFileName, "r")) == NULL) {//打开输入文件
        printf("can not open this src file!\n");
        exit(0);
    }
    if ((outputfp = fopen(destFileName, "w")) == NULL) {//打开输出文件
        printf("can not open this dest file!\n");
        exit(0);
    }
    while (!feof(inputfp)) {//feof函数用于检测文件读写是否到达末尾
        ch = fgetc(inputfp);//src字符读取
        fputc(ch, outputfp);//dest字符写入
    }
    fclose(inputfp);
    fclose(outputfp);
    return 0;
}
2.向文件读写字符串

image-20230317233447063

注:fgets和fputs函数功能类似于gets和puts函数,只是gets和puts以终端为读写对象,而fgets和fputs以指定的文件为读写对象。

  • 从键盘读入若干个字符串,对它们按字母大小顺序排序,然后把排好序的字符串送到磁盘文件中进行保存。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>

int main() {
    FILE *fp;
    char str[3][20];
    char temp[20];
    int n = 3;
    printf("please enter your strings:\n");
    for (int i = 0; i < n; ++i) gets(str[i]);//从标准输入设备中获取字符串 并用str[i]指针对其指向
    //1.对字符串进行排序(直接选择排序)
    for (int i = 0; i < n - 1; ++i) {
        int max = i;
        for (int j = i + 1; j < n; ++j) {//寻找i及其之后的字符串中最大的字符串 并将其赋值给max
            if (strcmp(str[max], str[j]) > 0) max = j;
        }
        if (max != i) {//将最大的字符串放在有序的位置
            strcpy(temp, str[i]);
            strcpy(str[i], str[max]);
            strcpy(str[max], temp);
        }
    }
    //2.打开文件
    if ((fp = fopen("string.txt", "w")) == NULL) {
        printf("can not open the file!\n");
        exit(0);
    }
    //3.写入数据
    for (int i = 0; i < n; ++i) {
        fputs(str[i], fp);
        fputs("\n", fp);
    }
    return 0;
}
  • string.txt中将数据读会到字符串数组中,并将其打印在屏幕上:
#include<stdio.h>
#include<stdlib.h>

int main() {
    FILE *fp;
    char str[3][50];
    //1.打开文件
    if ((fp = fopen("string.txt", "r")) == NULL) {
        printf("can not open the file\n");
        exit(0);
    }
    //2.将文件中的数据读入数组
    int index = 0;
    while (fgets(str[index], 50, fp) != NULL) index++;
    //3.将数组中的数据进行打印
    for (int i = 0; i < 3; ++i) printf("%s", str[i]);
    fclose(fp);
    return 0;
}
3.格式化方式读写文件

注:fprintf和fscanf与printf和scanf的区别是其读写对象不是终端而是文件。

  • fprintf(文件指针, 格式字符串, 输出列表);
  • fscanf(文件指针, 格式字符串, 输入列表);

用fprintf和fscanf函数对磁盘文件进行读写使用方便,但是由于需要进行ACSII码与二进制形式之间的转换,花费时间较多。

因此在内存与磁盘进行频繁交换数据的情况下,最好不用。

而用fread与fwrite函数进行二进制的读写。

4.二进制方式读写文件

C语言允许使用fread函数从文件中读一个数据块,用fwrite函数向文件中写入一个数据块(读写以二进制形式进行)。

在向磁盘写入数据时,直接将内存中的数据原封不动的复制到磁盘文件上。

  • fread(buffer, size, count, fp);
  • fwrite(buffer, size, count, fp);
  • buffer:地址
  • size:要读写的字节数
  • count:要读写多少个数据项(每个数据项长度为size)
#include<stdio.h>

#define SIZE 5

struct Student {
    char name[20];
    int num;
    int age;
    char addr[50];
};

struct Student stu[SIZE];

void bwrite() {
    FILE *fp;
    if ((fp = fopen("stu.txt", "wb")) == NULL) {
        printf("can not open the file\n");
        return;
    }
    for (int i = 0; i < SIZE; ++i) {
        if (fwrite(&stu[i], sizeof(struct Student), 1, fp) != 1) {
            printf("file write error\n");
        }
    }
    fclose(fp);
}

void bread() {
    FILE *fp;
    if ((fp = fopen("stu.txt", "rb")) == NULL) {
        printf("can not open the file\n");
        return;
    }
    for (int i = 0; i < SIZE; ++i) {
        fread(&stu[i], sizeof(struct Student), 1, fp);
    }
    fclose(fp);
}

int main() {
    printf("please enter data of the student:\n");
    for (int i = 0; i < SIZE; ++i) scanf("%s %d %d %s", stu[i].name, &stu[i].num, &stu[i].age, stu[i].addr);
    bwrite();
    printf("current data of the student:\n");
    bread();
    for (int i = 0; i < SIZE; ++i) printf("%-10s%4d%4d%10s\n", stu[i].name, stu[i].num, stu[i].age, stu[i].addr);
    return 0;
}

image-20230318114524521

三、随机读写数据文件

随机访问不是按数据在文件中的物理位置次序进行读写的,而是可以对任何位置上的数据进行访问,这种方法比顺序访问效率高很多。

1.文件位置标记

为了对读写进行控制,系统为每个文件设置了一个文件读写位置标记(文件位置标记),

image-20230318115135599

可以根据读写需要,人为的移动文件位置标记的位置,即随机读写。

  • rewind:作用是使文件位置标记重新指向文件开头,无返回值。

    FILE *fp1, *fp2;
    fp1 = fopen("src.txt", "r");
    fp2 = fopen("dest.txt", "w");
    while (!feof(fp1)) putchar(getc(fp1));//将文件内容输出到屏幕
    rewind(fp1);
    while (!feof(fp1)) putc(getc(fp1), fp2);//将文件内容拷贝到dest.txt
    fclose(fp1);
    fclose(fp2);
    
  • fseek:用于改变文件位置标记,fseek一般用于二进制文件,fseek(文件指针, 位移量, 起始点);
    image-20230318115849431

  • ftell:作用是得到流式文件中文件位置标记的当前位置,用相对于文件开头的位移量来表示。出错返回-1

2.随机读写
  • 在磁盘上存有10个学生的数据,要求利用文件随机读写将1、3、5、7、9个学生的数据输出到屏幕上。
FILE *fp;
if ((fp = fopen("stu.txt", "rb")) == NULL) {
    printf("can not open the file\n");
    exit(0);
}
for (int i = 0; i < SIZE; i += 2) {
    fseek(fp, i * sizeof(struct Student), 0);//移动文件位置标记
    fread(&stu[i], sizeof(struct Student), 1, fp);//读取一个数据块到结构体变量
    printf("%-10s%4d%4d%10s\n", stu[i].name, stu[i].num, stu[i].age, stu[i].addr);
}
fclose(fp);
3.读写错误检测
  • ferror(fp)

在调用各种输入输出函数(putc、getc、fread、fwrite)时如果出现错误,除了函数返回值有所反映外,还可以使用ferror函数检查。

如果返回值为0表示没有读写错误,如果返回非零值表示读写出错。

注:在调用1个输出输出函数后,需要立即检查ferror函数的返回值,否则错误信息会丢失。

  • clearerr(fp)

clearerr函数的作用是使文件错误标志文件结束标志置为0,

在输入输出函数出现错误ferror值为一个非零值时,应立即调用clearerror(fp),以便在进行下一次检查。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值