[笔记]指针与文件

文件与指针的关系,只是库函数的使用,在这里只要求掌握库函数对文件操作的流程,各项语言不同,在使用的过程中会出现很多错误:比如文件异常,打开途中异常,在高级语言:java/C++中加入了异常机制来处理。

我们来看看如何实现:

  • 步骤1:定义指向File结构体的指针。
  • 步骤2:打开写入或读取的文件
  • 步骤3:调用文件的写入或读取的库函数,以完成将数据写入到文件,或从文件中读取数据
  • 步骤4:关闭文件

其中有关文件的I/O的库函数,可分类为字符,字符串,格式化及记录等。

常用库函数:

字符:

  • 写入函数:fputc,putc
  • 读取函数:fgetc,getc

字符串:

  • 写入函数:fputs
  • 读取函数:fgets

格式化:

  • 写入函数:fprintf
  • 读取函数:fscanf

记录

  • 写入函数:fwrite
  • 读取函数:fread

其中以格式化的fprintf与fscanf函数最常用,因为它也可以用来处理字符,字符串以及记录的写入与读取

我们用一段代码来表示:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
    char id[10];
    int score;
    FILE* fptr;

    if ((fptr = fopen("score.dat", "w")) == NULL)
    {
        printf("无法打开score.dat");
        exit(1);
    }//判断文件是否打开成功
    /*利用不定数循环,当id和score为-1与-1时结束*/

    printf("请输入ID与Score(输入-1,-1结束):\n");
    do
    {
        
        printf("请输入id:");
        scanf("%s", id);
        printf("请输入score:");
        scanf("%d", &score);
        printf("\n");

        /*向score.dat文件中格式输入数据*/

        if (strcmp(id, "-1") != 0 && score != -1)
        {
            fprintf(fptr, "%s  %d\n", id, score);
        }
    } while (strcmp(id, "-1") != 0 && score != -1);

    fclose(fptr);/*先将缓冲区的数据写入磁盘,再关闭文件*/
    system("pause");

    return 0;

}

此范例程序的运行过程如下:

步骤一:定义指向FILE结构体的指针

程序以FILE *fptr; 定义指向FILE结构体的指针变量------fptr。之后文件处理动作都是以fptr来访问,

注意!FILE是一个结构体类型名,它是以typedef定义文件相关信息的结构体,此结构体在stdio(标准输出流)头文件中

struct _iobuf {
        char *_ptr;     //文件输入的下一个位置
        int   _cnt;     //当前缓冲区的相对位置
        char *_base;    //指基础位置(即是文件的其始位置)
        int   _flag;    //文件标志
        int   _file;    //文件的有效性验证
        int   _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
        int   _bufsiz;  //缓冲区大小
        char *_tmpfname;//临时文件名
        };
typedef struct _iobuf FILE;

步骤二:打开写入或读取文件

程序中利用fopen函数打开一个写入(w)文件,如下所示

img

当文件打开失败时,fptr会收到fopen函数返回的NULL,若成功,fopen将会返回文件在缓冲区内的首地址,fptr将会指向它,注意:是想将fopen函数的返回值指定给fptr后,再判断是否为NULL

步骤三:调用写入库函数,将相关数据写入到文件。

此程序以fprintf函数,将输入的id和score写入fptr指针所指向的score.dat文件内,如下所示:

img

用户输入不为-1的时候,将数据从缓冲区写入文件内。

步骤四:关闭文件

程序以fclose(fptr);关闭所指向文件。

运行结果:

img

程序运行后,我们在终端输入id和score,直到值为-1的时候,程序结束

然后再利用if判断,不为-1的时候,每一次的循环都会将数据写入fptr所指向的文件内,然后进下一次循环

我们来看看文件:

img

在所属文件夹内,新创建了一个dat文件,我们用记事本打开:

img

则我们刚刚输入的文件被保存到了文件夹

接着利用fscanf函数,读取刚刚创建的数据

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{

    FILE* fptr;
    char id[10];
    int score;
    

    if ((fptr = fopen("score.dat", "r")) == NULL)
    {
        printf("无法打开score.dat");
        exit(1);
    }//判断文件是否打开成功
    printf("%s \t%s", "id", "score\n\n");
    printf("=======================\n\n");
    while (fscanf(fptr, "%s %d", id, &score) != EOF)
    {
        printf("%s \t %d\n", id, score);
    }

    fclose(fptr);/*先将缓冲区的数据写入磁盘,再关闭文件*/
    system("pause");

    return 0;

}

与fprintf函数相对应的函数是fscanf函数,其语法相同,如下所示:

img

当fscanf函数遇到文件结束时,返回值EOF,利用此值判断文件是已达终点,若是,则结束文件的读取:否则,将读取的数据显示在屏幕上

运行结果:

img

从结果中我们可以看到,fscanf函数将fptr所指向的文件中的值以字符串和整型格式存入id与score变量内,与scanf函数不同的是,fscanf是从文件中读取值存入变量内,而scanf是从键盘输入终端中读取值。

fwrite与fread函数

若要写入和读取记录或数组的数据时,我们常以fwrite函数和fread函数进行处理.

fwrite函数的语法如下:

fwrite(&student,sizeof(student),1,fptr);

它共有四个参数,fptr指针是最后一个参数,其功能是将存放在student地址的结构体,写入fptr所指向的student.rec文件,每次写入一项,其大小是sizeof(student)。

我们用一段代码来解释

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<conio.h>
int main()
{
    struct node
    {
        char name[10];
        int score;
    };
    struct node student;
    FILE* fptr;

    char scorebit[81];
    if ((fptr = fopen("student.txt", "wb")) == NULL)
    {
        printf("\n文件错误\n");
        exit(0);
    }
    do
    {
        printf("请输入名字:");
        gets(student.name);
        printf("请输入分数:");
        gets(scorebit);

        student.score = atoi(scorebit);
        fwrite(&student, sizeof(student), 1, fptr);
        printf("继续输入请按Y\n");
    } while (_getch() == 'y');

    fclose(fptr);
    system("pause");
    return 0;
}

核心代码就那一句:

img

我们来看看文件内的数据

img

将存入在student地址内的数据,写入到fptr所指向的文件内,每次写入一项,但是我们打开文件后:

img

发现数字变成乱码,因为该函数本就不是为文本类型文件准备的,它写入的是一个二进制块,则用fread函数才能输出原有的数据

用fread函数输出:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<conio.h>
int main()
{
    struct node
    {
        char name[10];
        int score;
    };
    struct node student;
    FILE* fptr;

    if ((fptr = fopen("student.txt", "rb")) == NULL)
    {
        printf("\n文件错误\n");
        exit(1);
    }

    char scorebit[81];

    while (fread(&student, sizeof(student), 1, fptr) == 1)
    {
        printf("%s\t%d\n", student.name, student.score);
    }



    fclose(fptr);
    system("pause");
    return 0;
}

运行结果:

img

在进行文本操作的时候,最好使用fprintf和fscanf进行文件写读,fwrite和fread进行的是二进制写读,一般用于媒体文件,而且太过于底层,了解就好!

对于fprintf和fscanf必须要熟练的使用!

当前文件指针指向何处

我们可以使用ftell函数得知文件的指针当前指到哪里,当然在使用该函数之前, 请先写入一个文件(下列用fwrite函数写入)

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<conio.h>
int main()
{
    struct node
    {
        char name[10];
        int score;
    };
    struct node student;
    FILE* fptr;

    if ((fptr = fopen("student.txt", "rb")) == NULL)
    {
        printf("\n文件错误\n");
        exit(1);
    }

    char scorebit[81];

    while (fread(&student, sizeof(student), 1, fptr) == 1)
    {
        printf("%s\t%d\n", student.name, student.score);
        printf("文件指针此时指向:%p\n", ftell(fptr));
    }



    fclose(fptr);
    system("pause");
    return 0;
}

核心代码:

img

在将文件读入结构体数据的时候,下一段语句将会告诉你文件指针指向何处:

img

ftell(fptr)函数返回目前文件指针fptr所在的地址,通常地址以2的n次方计算,

若要寻找student.txt文件中某一项的数据,可利用fseek函数完成,请看代码:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<conio.h>
int main()
{
    struct node
    {
        char name[10];
        int score;
    };
    struct node student;
    FILE* fptr;
    int number, length, total_txt;
    int offset;


    if ((fptr = fopen("student.txt", "rb")) == NULL)
    {
        printf("\n文件错误\n");
        exit(1);
    }

    fseek(fptr, 0, 2);//将指针位于文件尾
    length = ftell(fptr);//length用于记录fptr的位置 
    total_txt = length / sizeof(struct node);
        
        //length除以一个节点的长度得到文件中有多少个数据
    
        printf("目前有%d项数据在文件中\n", total_txt);


    do
    {
        printf("你要查找第几项:");
        scanf("%d", &number);
        offset = (number - 1) * sizeof(student);
        if (number > total_txt)
        {
            printf("未找到该数据\n");
        }
        else
        {
            fseek(fptr, offset, 0);
            fread(&student, sizeof(student), 1, fptr);
            printf("姓名:%s\n", student.name);
            printf("分数:%d\n", student.score);
        }
        printf("继续'y'\n");
    } while (_getch() == 'y');

    fclose(fptr);
    printf("\n");
    return 0;
}

运行结果:

img

fseek函数的语法如下:

fseek(fptr,offset,direction);

此函数共有三个参数,分别如下:

  1. fptr,为指向文件的指针。
  2. offset,为位移的字节数,其数据类型为long int
  3. direction,表示从何处开始查找,分下列三种

(1)SEEK_SET或0,表示从文件头开始查找

(2)SEEK_CUR或1,表示从当前的位置开始查找

(3)SEEK_END或2,表示从文件尾开始查找

fseek函数成功,返回0,否则返回非0值。

此程序先利用以下片段,得知文件有多少文件数据:

img

  1. 从文件尾开始,偏移0字节,作用于fptr指向的文件
  2. 再利用ftell得知文件指针指向的内存值
  3. 最后利用尾内存值除以一个文件数据的内存值得到长度。

img

这儿有个注意的点,offset作为偏移量,是从0开始,而不是从1开始,所以输入的值要-1再去乘以一个结构的内存值,得到需要偏移的字节数

fgetpos与fsetpos函数

除了ftell可告知目前文件指针的地址外,fgetpos函数也有同样的功能,而且还可以将此地址存起来,下次要将文件指针移到上次的地址时,只要调用fsetpos函数就可以轻易的完成

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main()
{
    FILE* fp;
    fpos_t position;

    fp = fopen("file.txt", "w+");//指针指向file文件的首地址
    fgetpos(fp, &position);//将positio指定的地址传入fp
    fputs("Hello, World!", fp);

    fsetpos(fp, &position);
    fputs("这将覆盖之前的内容", fp);
    fclose(fp);

    return(0);
}

让我们编译并运行上面的程序,这将创建一个文件 file.txt,它的内容如下。首先我们使用 fgetpos() 函数获取文件的初始位置,接着我们向文件写入 Hello, World!,然后我们使用 fsetpos() 函数来重置写指针到文件的开头

我们再来看一段案例:

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
    FILE* fptr;
    
    fpos_t curpos;

    char buffer1[81], buffer2[81], buffer3[81];
    
    if ((fptr = fopen("bufferArea.txt", "r")) == NULL)
    {
        printf("error:0\n");
        system("pause");
        exit(1);
    }

    if (fgetpos(fptr, &curpos) != 0)
    {
        perror("失败!\n");
    }
    printf("curpos目前指向的地址是:%d\n", curpos);
    printf("fptr目前指向的地址是%d\n\n\n", ftell(fptr));

    fgets(buffer1, 10, fptr);

    printf("curpos目前指向的地址是:%d\n", curpos);
    printf("fptr目前指向的地址是%d\n", ftell(fptr));
    printf("buffer:%s\n\n", buffer1);

    fgets(buffer2, 8, fptr);

    printf("curpos目前指向的地址是:%d\n", curpos);
    printf("fptr目前指向的地址是%d\n", ftell(fptr));
    printf("buffer2:%s\n\n", buffer2);


    printf("重置指针\n");
    if (fsetpos(fptr, &curpos) != 0)
    {
        perror("失败\n");
    }
    printf("curpos目前指向的地址是:%d\n", curpos);
    printf("fptr目前指向的地址是%d\n\n\n", ftell(fptr));

    printf("读取27字符后.....\n\n");

    fgets(buffer3, 27, fptr);
    printf("curpos目前指向的地址是:%d\n", curpos);
    printf("fptr目前指向的地址是%d\n", ftell(fptr));
    printf("buffer3:%s\n", buffer3);


    fclose(fptr);

    system("pause");
    return 0;

}

从程序中可以很清晰的看到,在文件指针还未对文件进行任何操作的时候。

  1. 文件指针指向首地址,这时利用fgetpos函数保存该地址
  2. 随后对文件进行读取操作
  3. 先是从文件中提取了9个字符存入buffer1(在最后加上一个'0')所以为10个
  4. 再从文件中提取了7个字符存入到了buffer2中,这时指针已经走到了16个字符的位置
  5. 再通过fsetpos函数找到之前存入的位置

运行结果:

img

rewind函数

若要将文件指针反转到文件头,则可调用rewind函数,其语法如下:

rewind(fptr);

表示将fptr指针重置到文件头,其意义与fseek(fptr,offset,0)的效果一样

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
int main()
{
    char a[10];
    char b[10];
    int tell;
    FILE* fp;
    
    fp = fopen("rewind.txt", "r");
    
    if (fp == NULL)
    {
        printf("error\n");
        exit(0);
    }

    fscanf(fp, "%s\n", a);
    printf("%s\n", a);
    tell = ftell(fp);

    printf("%d\n", tell);
    
    rewind(fp);
    
    fscanf(fp, "%s\n", b);
    printf("\n%s\n", b);
    tell = ftell(fp);
    
    printf("%d\n", tell);

    fclose(fp);
    system("pause");
    return 0;
}

先看看需要打开的文本内的数据:

img

核心代码:

img

运行结果:

img

如果不加rewind,它将会输出第二段字符串

img

运行结果:

img

当第一个fscanf执行完后,程序将abcd0n六个字符传入给数组a

这时已经走到6,如果不使用rewind的话,指针位置将从6开始执行下一段语句

这时已经很清楚的能知道rewind函数用于将文件指针从头开始!

总结

主要文件操作包括以下函数:

img

库函数的使用熟悉就好,在linux中对文件的操作会体现的淋漓尽致,至于windows.....知道就好

本文链接:

https://nullcode.fun/118.html
1 + 7 =
快来做第一个评论的人吧~