C语言-文件

文件

1. 简介

文件用来存放程序、文档、音频、视频数据、图片等数据的。

文件就是存放在磁盘上的,一些数据的集合。

在 windows 下可以通过写字板或记事本打开文本文件对文件进行编辑保存。写字板和记事本是微软程序员写的程序,对文件进行打开、显示、读写、关闭。

作为一个程序员,必须掌握编程实现创建、写入、读取文件等操作对文件的操作是经常要用到的知识,比如:写飞秋软件传送文件 等

2. 分类

2.1 磁盘文件

指一组相关数据的有序集合,通常 存储在外部介质(如磁盘)上,使用时才调入内存。

如:我们在计算机上见到的文本文档,图片,视频,音频等文件

2.2 设备文件

在操作系统中把每一个与主机相连的 输入、输出设备 看着 是一个文件`,把 他们的输入、输出等同于对磁盘文件的读和写。

键盘:标准输入文件

屏幕:标准输出文件

其他设备:打印机,触摸屏,摄像头,音响等

在Linux操作系统中,每一个外部设备都在 /dev 目录下有对应的一个设备文件,我们程序中想要操作设备,就必须对其所对应的文件进行操作

3. 标准io库函数对磁盘文件的读写特点

3.1 文件缓冲区

缓冲区的目的提高磁盘使用效率

文件缓冲区是库函数申请的一段内存,由库函数对其进行操作,程序员没有必要知道存放在哪里,只需要知道对文件操作的时候的一些缓冲特点即可

3.2 刷新缓冲区的情况

3.2.1 行刷新

概念:

标准io库函数,往 标准输出(屏幕) 输出东西的时候是行缓冲,所谓的行缓冲,就是缓冲区碰到换行符的时候才刷新缓冲区

示例:

#include 
int main(int argc, char const *argv[])
{ 
    printf("啦啦啦");
    while(1);
    return 0;
}
3.2.2 满刷新

概念:

标准io库函数,往普通文件读写数据的,是全缓冲的,碰到换行符也不刷新缓冲区,即 缓冲区满了,才刷新缓冲区

示例:

#include 
int main(int argc, char const *argv[])
{ 
    printf("啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦123456");
    while(1);
    return 0;
}
3.2.3 强制刷新

概念:

通过调用fflush方法实现

示例:

#include 
int main(int argc, char const *argv[])
{ 
    printf("啦啦啦啦啦");
    fflush(stdout);
    while(1);
    return 0;
}
3.2.4 关闭刷新

概念:

程序关闭时,会刷新缓冲区中的内容

示例:

#include 
int main(int argc, char const *argv[])
{ 
    printf("啦啦啦啦啦");
    return 0;
}
3.2.5 无刷新

概念:

在读写文件的时候,通过 系统调用io(read write)对文件进行读写数据,这个时候是无缓冲的,即写数据会立马进入文件,读数据会立马进入内存

4. 磁盘文件分类

注意:

任何文件在物理上都是以二进制存储

4.1 以编码方式分为:

  • 二进制文件:基于值编码

    如:
        音频文件(mp3):二进制文件
        图片文件(bmp)文件,一个像素点由两个字节来描述*****######&&&&&
        *代表红色的值
        #代表绿色的值
        &代表蓝色的值
    二进制文件以位来表示一个意思。
    
  • 文本文件:基于字符编码

    如:
    	歌词文件(lrc):文本文件
    

4.2 值编码与字符编码的区别

5:
- 值编码:0000 0101
- 字符编码:对应的AScLL码(win+r,输入charmap查看):0x0035(十六进制) 0011 0101

4.3 二进制文件与文本文件对比

译码:

  • 文本文件编码基于字符定长,译码容易些;
  • 二进制文件编码是变长的,译码难一些(不同的二进制文件格式,有不同的译码方式)。

空间利用率:

  • 二进制文件用一个比特来代表一个意思(位操作);
  • 而文本文件任何一个意思至少是一个字符。
  • 二进制文件,空间利用率高。

可读性:

  • 文本文件用通用的记事本工具就几乎可以浏览所有文本文件
  • 二进制文件需要一个具体的文件解码器,比如读 BMP 文件,必须用读图软件。

总结一下:

  • 文件在硬盘上存储的时候,物理上都是用二进制来存储的。
  • 咱们的 标准io库函数,对文件操作的时候,不管文件的编码格式(字符编码、或二进制),而 是按字节对文件进行读写,所以咱们管文件又叫流式文件,即把文件看成一个字节流。

5. 文件指针

5.1 简介

文件指针 在程序中用来标示(代表)一个文件的,在 打开文件的时候得到文件指针文件指针 就用来代表 打开的文件

咱们对文件进行读、写、关闭等操作的时候,对文件指针进行操作即可,即咱们将文件指针,传给读、写、关闭等函数,那些函数就知道要对哪个文件进行操作。

5.2 定义文件指针

语法:

FILE * 指针变量名;

注意事项:

  • FILE 为大写,需要包含

  • FILE 是系统使用 typedef 定义出来的有关文件信息的一种 结构体类型,结构中含有文件名文件状态文件当前位置等信息

  • 一般情况下,我们 操作文件前 必须 定义一个文件指针 标示 我们将要操作的文件, 实际编程中使用库函数操作文件,无需关心 FILE 结构体的细节 ,只需要 将文件指针传给 io 库函数,库函数再 通过 FILE 结构体里的信息对文件进行操作 。

  • FILE在studio.h文件中声明

    typedef struct
    {
        short level; //缓冲区“满”或“空”的程度
        unsigned flags; //文件状态标志
        char fd; //文件描述符
        unsigned charhold; //如无缓冲区不读取字符
        short bsize; //缓冲区的大小
        unsigned char *buffer; //数据缓冲区的位置
        unsigned ar*curp; //指针,当前的指向
        unsigned istemp; //临时文件,指示器
        shorttoken; //用于有效性检查
    }FILE;
    //在缓冲文件系统中,每个被使用的文件都要在内存中开辟一块 FILE 类型的区域, 存放与操作文件相关的信息
    

补充:

c 语言中有三个特殊的文件指针 无需定义,在程序中可以直接使用

  • stdin: 标准输入 默认为当前终端(键盘)
    • 我们使用的 scanf、getchar 函数默认从此终端获得数据
  • stdout:标准输出 默认为当前终端(屏幕)
    • 我们使用的 printf、puts 函数默认输出信息到此终端
  • stderr:标准错误输出设备文件 默认为当前终端(屏幕)
    • 当我们程序出错使用:perror 函数时信息打印在此终端

5.3 使用步骤

1、打开文件获取文件指针
2、操作文件指针
	读 写
3、关闭文件

5.4 操作文件函数

5.4.1 fopen打开文件

作用:打开文件

函数:

FILE *fopen(const char * path,const char * mode);

头文件:

参数:

  • path:打开的文件地址
    • 相对路径
    • 绝对路径
  • mode:打开的模式
    • r:只读
      • 特点:文件不存在返回NULL,文件存在返回文件指针
    • w:只写
      • 特点:文件不存在,创建文件。文件存在清空文件原内容,在打开,文件打不开(如:只读文件),返回NULL
    • a:追加
      • 特点:文件不存在,创建文件。文件存在原文件末尾操作。
    • +:同时以读写打开指定文件
    • r+:以可读、可写的方式打开文件(不创建新文件)
    • w+:以可读、可写的方式打开文件(文件不存在,创建文件,文件存在清空原内容,打开)
    • a+:以添加方式打开文件,打开文件并在末尾更改文件(如果文件不存在,则创建文件)
    • b:二进制文件
    • t:文本文件
    • rb:二进制文件只读
    • wb:二进制文件只写
    • ab:二进制文件追加
5.4.2 fclose关闭文件

作用:关闭文件

函数:

int fclose(FILE *fp);

参数:

  • fp:要关闭的文件指针

返回值:

  • 0:关闭成功
  • 非0:关闭失败

示例:

#include 
int main(int argc, char const *argv[])
{ 
    //FILE * f01 = fopen("a.txt","r+");
    FILE * f02 = fopen("/home/gl/a.txt","r+");
    if (f02 == NULL)
    {
        printf("找不到指定文件\n");
        return;
    } 
    else
    {
    	printf("找到指定文件\n");
    } 
    int num = fclose(f02);
    if (num == 0)
    {
    	printf("关闭成功\n");
    } 
    else
    {
    	printf("关闭失败");
    } 
    return 0;
}
5.4.3 fflush冲刷缓冲区

语法:一般用于写入文件时

int fflush(FILE *stream);

参数:

  • stream:要冲刷的文件指针
5.4.4 fgetc/ fputc

fgetc 作用:一次读取一个字符

函数 :

int fgetc(FILE *stream);

参数:

  • stream:读取的文件指针

返回值:

  • 读取到的字符

注意:

  • 以t的方式:读到文件结尾返回 EOF,EOF是在stdio.h文件中定义的 符号常量,值为-1。再做判断时,不需要用引号包裹。
  • 以b的方式:读到文件结尾,使用 feof(后面会讲)判断结尾

示例:

#include 
int main(int argc, char const *argv[])
{
    FILE *f = fopen("a.txt", "r");
    if(f == NULL)
    {
        printf("文件打开失败\n");
        return 0;
    }
    int x;
    while ((x = fgetc(f)) != EOF)
    {
        printf("%c",x);
    }
    int c = fclose(f);
    if (c == 0)
    {
        printf("关闭成功\n");
    }
    return 0;
}
//helloworld关闭成功

fputc 作用:一次写入一个字符

函数:

int fputc(int c, FILE *stream)

参数:

  • c:写入的数据
  • stream:写入的文件指针

返回值:

  • 如果输出成功,则 返回输出的字节值
  • 如果 输出失败,则返回一个EOF

示例:

#include 
int main(int argc, char const *argv[])
{
    FILE *f = fopen("b.txt", "w");
    if(f == NULL)
    {
        printf("文件打开失败\n");
        return 0;
    }
    int res = fputc('a', f);
    fflush(f);  //强制冲刷缓冲区
    if (res == EOF)
    {
        printf("写入失败\n");
    }
    int c = fclose(f);
    if (c == 0)
    {
        printf("关闭成功\n");
    }
    return 0;
}
5.4.5 fgets/ fputs

fgets 作用:一次读取一个字符串

函数:

char *fgets(char* s,int size,FILE *stream);

参数:

  • s:存储 读取到的字符串的 变量
  • size:这是要读取的最大字符数(包括最后的空字符)。通常是使用以 str传递的数组长度
  • stream:读取的文件指针

返回值:

  • 成功返回读取的字符
  • 失败返回NULL

注意:

  • 读取到换行或文件结束

示例:

#include 
int main(int argc, char const *argv[])
{
    FILE *f = fopen("a.txt", "r");
    char str[30];
    char *r_str = fgets(str, 30, f);
    if (r_str == NULL)
    {
        printf("读取失败!\n");
    }
    printf("%s\n", r_str);
    int c = fclose(f);
    if (c == 0)
    {
        printf("关闭成功\n");
    }
    return 0;
}

在这里插入图片描述

fputs 作用:一次写入一个字符串

语法:

int fputs(const char *s, FILE *stream);

参数:

  • s:写入的字符串
  • stream:写入的文件指针

返回值:

  • 成功:返回写入的字节数
  • 失败:返回-1

示例:

#include 
int main(int argc, char const *argv[])
{
    FILE *f = fopen("b.txt", "a");
    char *str = "张三";
    int res = fputs(str, f);
    if (res == -1)
    {
        printf("字符串写入失败!\n");
    }
    printf("字符串写入成功!\n");
    int c = fclose(f);
    if (c == 0)
    {
        printf("关闭成功\n");
    }
    return 0;
}
5.4.6 fread/ fwrite

fread 语法:

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

功能:

  • stream 所标示的文件中读取数据,一块是 size 个字节,共 nmemb 块存放到 ptr 指向的内存里

参数:

  • ptr:指向带有最小尺寸的size*nmemb字节的内存块的指针
  • size:读取的每个元素的大小,单位字节
  • nmemb:读取的元素个数
  • stream:读取的文件指针

返回值:

  • 实际读到的块数。

fwite:语法:

size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream);

功能:

  • ptr 指向的内存里的数据,向 stream 所标示的文件中写入数据,一块是 size 个字节,共 nmemb 块

参数:

  • ptr:这是指向要被写入的元素数组的指针。
  • size:这是要被写入的每个元素的大小,以字节为单位。
  • nmemb:这是元素的个数,每个元素的大小为 size 字节
  • stream:这是指向 FILE 对象的指针,该 FILE 对象指定了一个输出流。

返回值:

  • 实际写入的块数。

注意:

  • fwrite 函数是将内存中的数据原样输出到文件中(可以是结构体)。
  • fread 函数是将文件中的数据原样读取到内存里(可以是结构体)。

示例:

#include 
typedef struct stu
{
    char name[30];
    int age;
}STU;

void write()
{
    FILE *f = fopen("stuinfo", "w");
    if(f == NULL)
    {
        printf("文件打开失败\n");
        return;
    }
    STU stu[3];
    for (int i = 0; i < 3; i++)
    {
        printf("请输入第%d个学生的信息,格式:姓名 年龄\n", i);
        scanf("%s %d", stu[i].name, &stu[i].age);
    }
    int res = fwrite(stu, sizeof(STU), 3, f);
    fclose(f);
    printf("over---->res=%d\n", res);
}

void read()
{
    STU stu[3];
    FILE *f = fopen("stuinfo", "r");
    if(f == NULL)
    {
        printf("文件打开失败\n");
        return;
    }
    fread(stu, sizeof(STU), 3, f);
    fclose(f);

    for (int i = 0; i < 3; i++)
    {
        printf("%s %d\n", stu[i].name, stu[i].age);
    }
}

int main(int argc, char const *argv[])
{
    write();
    read();
    return 0;
}

运行结果:

C语言-文件_第1张图片

5.4.7 fscanf/ fprintf

fscanf 语法:

int fscanf(FILE *stream, const char *format, ...)

作用:从文件指针所标示的文件中获取输入 ( 即,从文件中格式化读取)

参数:

  • stream:文件指针
  • format:格式化字符串
  • …:输入数据列表

返回值:

  • 成功:该函数返回成功匹配和赋值的个数。如果到达文件末尾发生读错误,则返回EOF

fscanfscanf 函数类似:

  • scanf 是从键盘(标准输入)获取输入;
  • fscanf 是从文件指针所标示的文件中获取输入。

fprintf 语法:

int fprintf(FILE *stream, const char *format, ...)

功能:将数据输出到文件指针所指定的文件中 (即 格式化写入文件)

参数:

  • stream:文件指针
  • format:格式化字符串
  • …:输出数据列表

返回值:

  • 成功:则返回写入的字符总数,
  • 失败:返回一个负数。

fprintfprintf 函数区别:

  • printf 是将数据输出到屏幕上(标准输出);
  • fprintf 函数是将数据输出到文件指针所指定的文件中。

示例:

#include 
void write(){
    FILE * f = fopen("a2.txt","w");
    if (f == NULL)
    {
        return;
    } 
    fprintf(f,"姓名:%s\t性别:%s\t年龄:%d\n","张三","男",18);
    fprintf(f,"姓名:%s\t性别:%s\t年龄:%d\n","小丽","女",19);
    fprintf(f,"姓名:%s\t性别:%s\t年龄:%d\n","丽莎","女",20);
    fclose(f);
    printf("WRITEOVER\n");
} 
void read(){
    FILE * f = fopen("a2.txt","r");
    if (f == NULL)
    {
        return;
    } 
    int tag;
    char name[20];
    char sex[10];
    int age = 0;
    // fscanf(f,"姓名:%s\t性别:%s\t年龄:%d\n",name,sex,&age);
    // printf("姓名:%s\t性别:%s\t年龄:%d\n",name,sex,age);
    // fscanf(f,"姓名:%s\t性别:%s\t年龄:%d\n",name,sex,&age);
    // printf("姓名:%s\t性别:%s\t年龄:%d\n",name,sex,age);
    while ((tag = fscanf(f,"姓名:%s\t性别:%s\t年龄:%d\n",name,sex,&age)) !=EOF)
    {
        printf("姓名:%s\t性别:%s\t年龄:%d\n",name,sex,age);
        printf("tag:%d\n", tag);
    } 
    fclose(f);
} 
int main(int argc, char const *argv[])
{ 
    write();
    read();
    return 0;
}

运行结果:
C语言-文件_第2张图片
C语言-文件_第3张图片

6. 随机读写

  • 前面介绍的对文件的读写方式都是 顺序读写,即读写文件只能从头开始,顺序读写各个数据;
  • 但在实际问题中常要求 只读写文件中某一指定的部分,例如:读取文件第 200–300 个字节
  • 为了解决这个问题
    • 移动 文件内部的 位置指针 到需要读写的位置,再进行读写,这种读写称为随机读写;
    • 实现随机读写的关键是要按要求移动位置指针,这称为文件的定位

6.1 rewind复位读写位置(开头)

作用:设定文件指针位置为 文件开头

语法:

void rewind(FILE *stream)

参数:

  • stream:文件指针

示例:

![36-rewind](images/36-rewind.png)#include <stdio.h>
int main(int argc, char const *argv[])
{ 
    FILE *f = fopen("a.txt","r");
    char str[50];
    fgets(str,sizeof(str),f);
    printf("%s",str);
    rewind(f);
    fgets(str,sizeof(str),f);
    printf("%s",str);
    printf("\n");
    return 0;
}

结果:读写一次后,下次应该接着上次的位置,使用rewind()后第二次读写依旧在文件开头

在这里插入图片描述

6.2 fseek定位

作用:设置文件指针位置

语法:

int fseek(FILE *stream, long offset, int whence);

参数:

  • stream:文件指针
  • offset:相对于whence的偏移量,以起始点为基点,向前、后移动的字节数,正数往文件末尾方向偏移,负数往文件开头方向偏移。
  • whence:位置
    • 0:开始;
    • 1:当前位置;
    • 2:结尾。

6.3 ftell 测量文件字节

作用:获取指针偏移量,测量文件读写位置距文件开始有多少字节

语法:

long ftell(FILE *stream)

参数:

  • stream:文件指针

返回值:

  • 文件读写位置距文件开始的字节数

示例:

#include 
int main(int argc, char const *argv[])
{
    FILE *f = fopen("a.txt", "r");

    int l = fseek(f, 0, 2); //移动指针到文件末尾
    long len = ftell(f); //测量指针距离文件开头的位置,因为指针在末尾,所以此时获取的值就是文件的大小
    fclose(f);
    printf("文件长度:%ld\n", len);

    return 0;
}
// 文件长度:10

你可能感兴趣的:(C/C++,c语言)