C语言入门(十二)文件操作

本文介绍C语言中的文件操作,包括文件的基本概念、不同类型的文件、文件的打开与关闭、顺序读写、格式化读写、二进制读写及随机读写等。并通过多个实例演示了如何使用C语言进行文件操作。

计算机文件,是存储在某种长期储存设备或临时存储设备中的一段数据流,并且归属于计算机文件系统管理之下。

所谓“长期储存设备”一般指磁盘、光盘、磁带等。而“短期存储设备”一般指计算机内存。需要注意的是,存储于长

期存储设备的文件不一定是长期存储的,有些也可能是程序或系统运行中产生的临时数据,并于程序或系统退出后删除。

文件是什么:.exe .txt .ppt .jpg .mp4 .avi

Linux和Unix操作系统:everything is a file

 

C语言有两种文件:文本文件(看得懂的文件)和二进制文件(看不懂的文件)

打开和关闭文件:fopen函数和fclose函数(自己了解)

顺序读写:

读单个字符:fgetc函数(函数)和getc函数(宏定义)

写单个字符:fputc函数(函数)和putc函数(宏定义)

例子1

/*单个字符的读写操作*/

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp1;
    FILE *fp2;
    int ch;

    if((fp1 = fopen("hello.txt", "r")) == NULL)
    {
        printf("文件打开失败!\n");
        exit(EXIT_FAILURE);
    }
    
    if((fp2 = fopen("fishc.txt", "w")) == NULL)
    {
        printf("文件打开失败!\n");
        exit(EXIT_FAILURE);
    }

    while((ch = fgetc(fp1)) != EOF)
    {
        fputc(ch, fp2);
    }

    fclose(fp1);
    fclose(fp2);

    return 0;
}

写多个字符:fputs函数

读多个字符:fgets函数

例子2

/*多个字符的读写操作*/

#include <stdio.h>
#include <stdlib.h>

#define MAX 1024

int main(void)
{
    char buffer[MAX];
    FILE *fp;
    //char string = "To the new life";

    if((fp = fopen("hello.txt", "w")) == NULL)
    {
        printf("打开文件失败!\n");
        exit(EXIT_FAILURE);
    }    
      
    fputs("Line one: To the new life.\n", fp);
    fputs("Line two: My life.\n", fp);  
    /*会打印两次,因第三行只有EOF而fgets会选择保留先前的字符串并输出*/

    fclose(fp);

    if((fp = fopen("hello.txt", "w")) == NULL)
    {
        printf("打开文件失败!\n");
        exit(EXIT_FAILURE);
    }    

    while( !feof(fp) )
    {
        fgets(buffer, MAX, fp);
        printf("%s", buffer);
    }

    return 0;       
}

格式化读写文件:fscanf函数和fprintf函数

例子3

/*用time函数读取当前日期并使用fprintf()函数打印出来*/

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp;
    struct tm *P;
    time_t t;

    time(&t);
    p = localtime(&t);

    if((fp = fopen("hello.txt", "w")) == NULL)
    {
        printf("打开文件失败!\n");
        exit(EXIT_FAILURE);
    }

    fprintf(fp, "%d-%d-%d", 1900 + (*p).tm_year, l + (*p).tm_mon, (*p).tm_mday);

    fclose(fp);

    int year, month, day;
    
    if((fp = fopen("hello.txt", "r")) == NULL)
    {
        printf("打开文件失败!\n");
        exit(EXIT_FAILURE);
    }

    fscanf(fp, "%d-%d-%d", &year, &month, &day);
    printf("%d-%d-%d\n", year, month, day);

    fclose(fp);
    
    return 0;    
}

二进制形式读写文件

直接读写文件:fread函数和fwrite函数

例子4

/*用fwrite函数和fread函数将一个结构体写入到函数中并打印出来*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Date
{
    int year;
    int month;
    int day;
};

struct Book
{
    char name[30];
    char author[30];
    char publisher[30];
    struct Date date;
};

int main(void)
{    
    FILE *fp;
    struct Book *book_for_write;
	struct Book *book_for_read;
    
    book_for_write = (struct Book *)malloc(sizeof(struct Book));
    book_for_read = (struct Book *)malloc(sizeof(struct Book));

    if(book_for_write == NULL || book_for_read == NULL)
    {
        printf("分配内存失败!\n");
        exit(EXIT_FAILURE);
    }

    strcpy((*book_for_write).name, "C和指针");
    strcpy((*book_for_write).author, "Victor Lee");
    strcpy((*book_for_write).publisher, "北京大学出版社");
    (*book_for_write).date.year = 2010;
    (*book_for_write).date.month = 10;
    (*book_for_write).date.day = 1;

    if((fp = fopen("hello.txt", "w")) == NULL)
    {
        printf("文件打开失败!\n");
        exit(EXIT_FAILURE);
    }

    fwrite(book_for_write, sizeof(struct Book), 1, fp); //将结构体写入文件

    fclose(fp);  //写入完成,关闭文件

    if((fp = fopen("hello.txt", "r")) == NULL)
    {
        printf("文件打开失败!\n");
        exit(EXIT_FAILURE);
    }  

    fread(book_for_read, sizeof(struct Book), 1, fp); //用结构体将文件内容读出

    printf("书名:%s\n", (*book_for_read).name);
    printf("作者名:%s\n", (*book_for_read).author);
    printf("出版商:%s\n", (*book_for_read).publisher);
    printf("出版日期:%d-%d-%d\n", (*book_for_read).date.year, (*book_for_read).date.month, (*book_for_read).date.day);

    fclose(fp);

    return 0;
}

随机读写文件

ftell函数:获取位置指示器

rewind函数:将位置指示器移动到文件头的位置 如 rewind (fp);

fseek函数:用于设置文件流的指示器,可以偏移到任意位置;

例子5

/*获取位置指示器的值,知道位置指示器能实现随机读写(允许从文件的任意一个位置进行读写)*/

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp;

    if((fp = fopen("hello.txt", "w")) == NULL)
    {
        printf("文件打开失败!\n");
        exit(EXIT_FAILURE);
    }
    
    printf("%ld\n", ftell(fp));
    fputc('V', fp);
    printf("%ld\n", ftell(fp));
    fputs("ictor\n", fp);
    printf("%ld\n", ftell(fp));

    fclose(fp);

    return 0;
}

例子6

/*要求录入学生的姓名,学号和成绩到指定的文件中,并打印出第二位学生的个人信息*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Student
{    
    int num;
    char name[20];
    float score;
};

int main(void)
{
    FILE *fp;
    struct Student *s, *s1, *s2, *s3;  //可以用结构体数组,由用户自己输入
    
    s = (struct Student *)malloc(sizeof(struct Student));
    s1 = (struct Student *)malloc(sizeof(struct Student));
    s2 = (struct Student *)malloc(sizeof(struct Student));
    s3 = (struct Student *)malloc(sizeof(struct Student));

    (*s1).num = 1001;
    strcpy((*s1).name, "victor");
    (*s1).score = 89.0;
    
    (*s2).num = 1002;
    strcpy((*s2).name, "ramon");
    (*s2).score = 95.0;

    (*s3).num = 1003;
    strcpy((*s3).name, "kim");
    (*s3).score = 90.0;

    if((fp = fopen("stu.txt", "w")) == NULL)
    {
        printf("打开文件失败!\n");
        exit(EXIT_FAILURE);
    }

    fwrite(s1, sizeof(struct Student), 1, fp);
    fwrite(s2, sizeof(struct Student), 1, fp);
    fwrite(s3, sizeof(struct Student), 1, fp);

    fclose(fp);

    if((fp = fopen("stu.txt", "r")) == NULL)
    {
        printf("打开文件失败!\n");
        exit(EXIT_FAILURE);
    }

    fseek(fp, sizeof(struct Student), SEEK_SET);
    fread(s, sizeof(struct Student), 1, fp);

    printf("学号位%d的学生%s的成绩为%0.2f\n", (*s).num, (*s).name, (*s).score);

    fclose(fp);

    return 0;
}

使用fseek函数需要考虑可移植性问题:

1.对于以二进制模式打开的文件,fseek函数在某些操作系统可能不支持SEEK_END位置

2.对于以文本模式打开的文件,fseek函数的whence参数只能取SEEK_SET才是有意义的,

并且传递给offset参数要么是0,要么是上一次对同个文件调用ftell函数获得的返回值

 

标准流和错误处理

当一个程序在被执行时,C语言会自动打开三个面向终端的文件流:标准输入流(stdin) 标准输出流(stdout) 标准错误输出流(stderr)

例子7

/*当文件打开遇到错误时,将其错误信息输出到标准错误输出流*/

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    FILE *fp;
    
    if((fp = fopen("notexit.txt", "r")) == NULL)
    {
        printf("标准输出!\n");
        fputs("打开文件失败!\n", stderr);
        exit(EXIT_FAILURE);
    }

    

    fclose(fp);

    return 0;
}

重定向:由于标准输出和标准错误输出通常都是直接打印到屏幕上,为了区分它们,可以使用linux shell的重定向功能

重定向标准输入:<

重定向标准输出:>

重定向标准错误输出:2>

错误处理

错误指示器:ferror

使用clearerr函数可以人为地清除文件末尾指示器和错误指示器的状态

ferror函数只能检测是否出错,但无法获取错误原因。不过,大多数系统函数在出现错误的时候会将错误原因记录在errno(仅显示错误码)中 <errno.h>

perror函数可以直观地打印出错误原因,用法:perror("打开文件失败,原因是");

strerror函数:直接返回错误码对应的错误信息

IO缓冲区:与内存池原理类似

在调用fputs函数时,数据写在缓冲区中,在调用fclose函数时,数据才写入文件(可用阻塞函数验证,如getchar函数)

fflush函数:刷新缓冲区,适用于在关闭文件之前就将程序代码保存在文件中

 

标准IO缓冲提供三种类型的缓冲模式

按块缓存:全缓存,即在填满缓冲区后才进行实际的设备读写操作;

按行缓存:在接收到换行符之前,数据都是先缓存在缓冲区的;

不缓存:顾名思义,允许你直接读写设备上的数据。

setvbuf函数:用于指定一个数据流的缓存模式

例子8

#include <stdio.h>
#include <string.h>

int main(void)
{
    char buff[1024];
    memset(buff, '\0', sizeof(buff));
    setvbuf(stdout, buff, _IOFBF, 1024);

    fprintf(stdout, "Welcome to Wuhan!\n");
    fflush(stdout);

    fprintf(stdout, "输入任意字符后才会显示该行字符!\n");
    getchar();

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值