首页 关于 微信公众号
欢迎关注我的微信公众号

文件操作

文件概述

所有的文件都通过流进行输入、输出操作。与文本流和二进制流对应,文件可以分为文本文件和二进制文件两大类。

文件基本操作

文件的基本操作包括文件的打开和关闭。除了标准的输入、输出文件外,其他所有的文件都必须先打开再使用,而使用后也必须关闭该文件。

文件指针

文件指针是一个指向文件有关信息的指针,这些信息包括文件名、状态和当前位置,他们保存在一个结构体变量中。在使用文件时需要在内存中为其分配空间,用来存放文件的基本信息。该结构体类型是由系统定义的,C语言规定该类型为 FILE 型,其声明如下:

typedef	struct __sFILE {
	unsigned char *_p;	/* current position in (some) buffer */
	int	_r;		/* read space left for getc() */
	int	_w;		/* write space left for putc() */
	short	_flags;		/* flags, below; this FILE is free if 0 */
	short	_file;		/* fileno, if Unix descriptor, else -1 */
	struct	__sbuf _bf;	/* the buffer (at least 1 byte, if !NULL) */
	int	_lbfsize;	/* 0 or -_bf._size, for inline putc */

	/* operations */
	void	*_cookie;	/* cookie passed to io functions */
	int	(*_close)(void *);
	int	(*_read) (void *, char *, int);
	fpos_t	(*_seek) (void *, fpos_t, int);
	int	(*_write)(void *, const char *, int);

	/* separate buffer for long sequences of ungetc() */
	struct	__sbuf _ub;	/* ungetc buffer */
	struct __sFILEX *_extra; /* additions to FILE to not break ABI */
	int	_ur;		/* saved _r when _r is counting ungetc data */

	/* tricks to meet minimum requirements even when malloc() fails */
	unsigned char _ubuf[3];	/* guarantee an ungetc() buffer */
	unsigned char _nbuf[1];	/* guarantee a getc() buffer */

	/* separate buffer for fgetln() when line crosses buffer boundary */
	struct	__sbuf _lb;	/* buffer for fgetln() */

	/* Unix stdio files get aligned to block boundaries on fseek() */
	int	_blksize;	/* stat.st_blksize (may be != _bf._size) */
	fpos_t	_offset;	/* current lseek offset (see WARNING) */
} FILE;

从上面的结构中可以发现使用typedef定义了一个FILE为该结构体类型,在编写程序时可直接使用上面定义的 FILE 类型来定义变量,注意在定义变量是不必将结构体内容全部给出,只需要如下形式:

FILE *fp; // fp是一个指向 FILE 类型的指针变量

文件的打开

fopen 函数用来打开一个文件,打开文件的操作就是创建一个流。fopen 函数的原型在 stdio.h 中,起吊用的一般形式为:

FILE *fp;
fp = fopen(文件名,使用文件方式);

其中文件名就是要被打开的文件的名字,”使用文件方式“ 是指对打开的文件要进行读还是写。文件的大开发方式有如下:

文件使用方式 含义
r (只读) 打开一个文本文件,只允许读数据
w (只写) 打开或建立一个文本文件,只允许写数据
a (追加) 打开一个文本文件,并在文件末尾写数据
rb (只读) 打开一个二进制文件,只允许读数据
wb (只写) 打开或建立一个二进制文件,只允许写数据
ab (追加) 打开一个二进制文件,并在文件末尾写数据
r+ (读写) 打开一个文本文件,只允许读和写
w+ (读写) 打开或建立一个文本文件,允许读写
a+ (读写) 打开一个文件,允许读,火灾文件末追加数据
rb+ (读写) 打开一个二进制文件,允许读和写
wb+ (读写) 打开或建立一个二进制文件,允许读和写
ab+ (读写) 打开一个二进制文件,语序读,火灾文件末追加数据

如果使用 fopen 函数打开文件成功,则返回一个有确定指向的 FILE 类型指针;若打开失败,则返回 NULL 。通常打开失败的原因有以下几个方面:

文件的关闭

文件使用完毕后,应使用 fclose 函数将其关闭。 fclose 函数和 fopen 一样,原型也在 stdio.h 中,调用的一般形式为:

fclose(文件指针);

fclose 函数也返回一个值,当正常完成关闭文件操作时, fclose 函数返回值为 0 ,否则返回 EOF。 在程序结束之前应关闭所有文件,这样做的目的是防止因为没有关闭文件二造成数据流失。

文件的读写

fputc 函数

ch = fputc(ch,fp);

该函数的作用是把一个字符写到磁盘文件(fp所指向的文件) 中去。其中 ch 是要输出的字符,它可以是一个字符常量,也可以是一个字符变量。 fp是文件指针变量。如果函数输出成功则返回值就是输出的字符;如果失败,则返回 EOF 。

eg:

void writeCharToFile()
{
    FILE *fp;
    char ch;
    fp = fopen("/Users/qizhang/Desktop/exp01.txt", "w");
    if (fp == NULL) {
        printf("can not open file\n");
        exit(0);
    }
    ch = getchar();
    while (ch != '#') {
        fputc(ch, fp);
        ch = getchar();
    }
    fclose(fp);
}

fgetc 函数

ch = fgetc(fp);

该函数的作用是从指定的文件(fp指向的文件)读入一个字符赋给ch 。需要注意的是,该文件必须是以读或读写的方式打开。当函数遇到文件结束符时将返回一个文件结束标志 EOF .

eg: 从刚刚的文件中读数据

void readCharFromFile()
{
    FILE *fp;
    char ch;
    fp = fopen("/Users/qizhang/Desktop/exp01.txt", "r");
    ch = fgetc(fp);
    while (ch != EOF) {
        putchar(ch);
        ch = fgetc(fp);
    }
    fclose(fp);
}

fputs 函数

fputs 函数和 fputc 函数类似,区别在于 fputc 每次只向文件中写入一个字符,而 fputs 函数每次向文件中写入一个字符串。

fputs(字符串,文件指针);

该函数的作用是向指定的文件写入一个字符串,其中字符串可以是字符串常量,也可以是字符数组名、指针或变量。

eg:

void writeStringToFile()
{
    FILE *fp;
    char filename[30],str[30];
    printf("please input filename:\n");
    scanf("%s",filename);
    fp = fopen(filename, "w");
    if (fp == NULL) {
        printf("Can not open! \n press any key to continue:\n");
        getchar();
        exit(0);
    }
    printf("please input string:\n");
    getchar();
    gets(str);
    fputs(str, fp);
    fclose(fp);
}

fgets 函数

fgets 函数与 fgetc 函数类似,区别在于 fgetc 每次从文件中读出一个字符,而 fgets 函数每次从文件中读出一个字符串。

fgets(字符数组名,n,文件指针); 该函数的作用是从指定的文件中读一个字符串到字符数组中。 n表示所得到的字符串中红的字符的个数 (包含'\0')。

eg:

void readStringFromFile()
{
    FILE *fp;
    char filename[30],str[30];
    printf("please input filename:\n");
    scanf("%s",filename);
    fp = fopen(filename,"r");
    if(fp == NULL) {
        printf("can not open!\n press any key to continue\n");
        getchar();
        exit(0);
    }
    fgets(str, sizeof(str), fp);  // 只获取30个长度
    printf("%s",str);
    fclose(fp);
}

fprintf 函数

fprintf函数和fscanf函数与printf和scanf函数的作用相似,它们最大的区别就是读写的对象不同,fprintf和fscanf函数读写的对象不是终端而是磁盘文件。

ch = fprintf(文件类型指针,格式字符串,输出列表);
fprintf(fp,"%d",i); // 将整形变量i输出到fp指向的文件中

eg:

void writeToFile()
{
    FILE *fp;
    int i = 88;
    char filename[80];
    printf("please input filename:\n");
    scanf("%s",filename);
    fp = fopen(filename, "w");
    if (fp == NULL) {
        printf("can not open!\npress any key to continue\n");
        getchar();
        exit(0);
    }
    fprintf(fp, "%c",i); // 将88以字符形式写入fp所指的文件中
    fclose(fp);
}

fscanf 函数

fscanf函数的一般形式如下:

fscanf(文件类型指针,格式字符串,输入列表);
fscanf(fp,"%d",&i);

它的作用是读入fp所指向的文件中的i的值。

eg:

void readOperationUSEfscanf()
{
    FILE *fp;
    char i,j;
    char filename[30]; // 定义一个字符型数组
    printf("please input filename:\n");
    scanf("%s",filename); // 输入文件名
    fp = fopen(filename, "r");
    if (fp == NULL) {
        printf("can not open!\npress any key to continue\n");
        getchar();
        exit(0);
    }
    for (i = 0; i < 5; i++) {
        fscanf(fp, "%c",&i);
        printf("%d is %5d\n",i+1);
    }
    fclose(fp);
}

fread函数和fwrite函数

fputc 和 fgetc 函数每次只能读写文件中的一个字符,但是在编写程序的过程中往往需要对整块数据进行读写,例如对一个结构体类型变量值进行读写。此时就需要使用 fread 和 fwrite 函数。

fread 函数的一般形式如下:

fread(buffer,size,count,fp);

该函数的作用是从 fp 所指的文件中读入 count 次,每次读 size 字节,读入的字节存在 buffer 地址中。

fwrite 函数的一般形式如下:

fwrite(buffer,size,count,fp);

它的作用是将 buffer 地址开始的信息输出 count 次,每次写 size 字节到 fp 所指向的文件中。参数说明如下:

eg:

fread(a,2,3,fp);

其含义是从 fp 所指文件中每次读两个字节送入实数组a中,连续读 3 次。

fwrite(a,2,3,fp);

其含义是将 a 数组中的信息每次输出两个字节到 fp 所指向的文件中,连续输出 3 次。

struct address_list
{
    char name[10];
    char adr[20];
    char tel[15];
}info[100];

void save(char *name,int n)
{
    FILE *fp;
    int i;
    fp = fopen(name, "wb");
    if (fp == NULL) {
        printf("can not open file\n");
        exit(0);
    }
    for (int i = 0; i < n; i++) {
        if (fwrite(&info[i], sizeof(struct address_list), 1, fp) != 1) {
            printf("file write error\n");
        }
        fclose(fp);
    }
}

void show(char *name,int n)
{
    int i;
    FILE *fp;
    fp = fopen(name, "rb");
    if (fp == NULL) {
        printf("can not open file\n");
        exit(0);
    }
    for (i = 0; i < n; i++) {
        fread(&info[i], sizeof(struct address_list), 1, fp);
        printf("%15s%20s%20s\n",info[i].name,info[i].adr,info[i].tel);
    }
    fclose(fp);
}

int main()
{
    int i,n;
    char filename[50];
    printf("how may ?\n");
    scanf("%d",&n);
    printf("please input filename:\n");
    scanf("%s",filename);
    printf("please input name,address,telephone:\n");
    for (int i= 0; i < n; i++) {
        printf("NO%d",i+1);
        scanf("%s%s%s",info[i].name,info[i].adr,info[i].tel);
        save(filename, n);
    }
    
    show(filename, n);
    
    return 0;
}

文件的定位

在对文件进行操作时,往往不需要从头开始,秩序对其中指定的内容进行操作,这是就需要使用文件定位函数来实现文件的随机读取。

fseek 函数

借助缓冲型 I/O 系统中的 fseek 函数可以完成随机读写操作。 fseek 函数的一般形式如下:

fseek(文件类型指针,位移量,起始点);

该函数的作用是移动文件内部位置指针。其中,“文件类型指针”指向被移动文件,“位移量”表示移动的字节数,要求位移量是 long 类型数据,以便在文件长度大于 64KB 时不会出错。当用常量表示位移量时,要求加后缀 “L” ;参数“起始点”表示从何处开始计算位移量,规定的起始点有3种:文件首、文件当前位置和文件尾。如图:

起始点 表示符号 数字表示
文件手 SEEK-SET 0
当前位置 SEEK-CUR 1
文件末尾 SEEK-END 2
fseek(fp,-20L,1);  表示将位置指针从当前位置向后退20个字节。fseek 函数一般用于二进制文件。在文本文件中由于要进行转换,往往计算的位置会出现错误。文件的随机读写在移动位置指针之后进行,即可用前面介绍的任一种读写函数进行读写。

eg:

void fseekDemo()
{
    FILE *fp;
    char filename[30],str[50];
    printf("please input filename:\n");
    scanf("%s",filename);
    fp = fopen(filename, "wb");
    if (fp == NULL) {
        printf("can not open!\n press any key to continue\n");
        getchar();
        exit(0);
    }
    printf("please input string:\n");
    getchar();
    gets(str);
    fputs(str, fp);
    fclose(fp);
    
    fp = fopen(filename, "rb");
    if (fp == NULL) {
        printf("can not open!\npress any key to continue\n");
        getchar();
        exit(0);
    }
    fseek(fp, 5L, 0); // 将文件指向距文件首5个字节的位置,指向字符串中的第6个字符
    fgets(str, sizeof(str), fp);
    putchar('\n');
    puts(str);
    fclose(fp);
}

rewind 函数

void rewind(文件类型指针); 该函数的作用是使位置指针重新返回文件的开头,该函数没有返回值。

eg:输出文件中的内容

void rewindDemo()
{
    FILE *fp;
    char ch,filename[50];
    printf("please input filename:\n");
    scanf("%s",filename);
    fp = fopen(filename, "r");
    if (fp == NULL) {
        printf("can not open this file.\n");
        exit(0);
    }
    ch = fgetc(fp);
    while (ch != EOF) {
        putchar(ch);
        ch = fgetc(fp);
    }
    rewind(fp);
    ch = fgetc(fp);
    while (ch != EOF) {
        putchar(ch);
        ch = fgetc(fp);
    }
    fclose(fp);
}

ftell 函数

long ftell(文件类型指针); 该函数的作用是得到流式文件中的当前位置,用相对于文件开头的位移量来表示。当 ftell 函数返回值为 -1L 时,表示出错。

eg:(求字符串的长度)

void getStrLength()
{
    FILE *fp;
    int n;
    char ch,filename[50];
    printf("please input filename:\n");
    scanf("%s",filename);
    fp = fopen(filename, "r");
    if (fp == NULL) {
        printf("can not open this file.\n");
        exit(0);
    }
    ch = fgetc(fp);
    while (ch != EOF) {
        putchar(ch);
        ch = fgetc(fp);
    }
    n = ftell(fp);
    printf("\nthe length of the string is:%d\n",n);
    fclose(fp);
}

Blog

Opinion

Project

-->