Skip to content

预备知识

一个文件通常就是磁盘上一段命名的存储区。但是对于操作系统来说,文件就会更复杂一些。例如,一个⼤文件可以存储在一些分散的区段中,或者还会包含一些操作系统可以确定其文件类型的附加数据,但是这些是操作系统,而不是我们程序员所要关⼼的事情。我们应该考虑如何在C程序中处理文件。

磁盘文件和设备文件

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

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

磁盘文件的分类

计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储。

从用户或者操作系统使用的角度(逻辑上)把文件分为:

  • 文本文件:基于字符编码的文件
  • 二进制文件:基于值编码的文件

文本文件

基于字符编码,常见编码有ASCII、UNICODE等,一般可以使用文本编辑器直接打开。数 5678的以ASCII存储形式(ASCII码)为:00110101 00110110 00110111 00111000

二进制文件

基于值编码,⾃⼰根据具体应用,指定某个值是什么意思。把内存中的数据按其在内存中的存储 形式原样输出到磁盘上。数5678的存储形式(二进制码)为:00010110 00101110

文件指针

在C语⾔中用一个指针变量指向一个文件,这个指针称为文件指针。

我们知道,文件是由操作系统管理的单元。当我们想操作一个文件的时候,让操作系统帮我们打开文件,操作系统把我们指定要打开文件的信息保存起来,并且返回给我们一个指针指向文件的信息。文件指针也可以理解为代指打开的文件。这个指针的类型为FILE类型。该类 型定义在stdio.h头文件中。通过文件指针,我们就可以对文件进行各种操作。

对于每一个ANSI C程序,运行时系统必须提供⾄少三个流-标准输⼊(stdin)、标准输出 (stdout)、标准错误(stderr),它们都是一个指向FILE结构的指针。标准输⼊是缺省情况下的输 ⼊来源,标准输出时缺省情况下的输出设置。具体缺省值因编译器而异,通常标准输⼊为键盘设备、标准输出为终端或者屏幕。

C语⾔中有由系统默认打开,用户⽆需定义即可直接使用:

  • stdin: 标准输⼊,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
  • stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
  • stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。

ANSI C并未规定FILE的成员,不同编译器可能有不同的定义。VS下FILE信息如下:

c
struct _iobuf {
    char *_ptr;     //文件输⼊的下一个位置 
    int _cnt;       //剩余多少字符未被读取 
    char *_base;    //指基础位置(应该是文件的其始位置) 
    int _flag;      //文件标志 
    int _file;      //文件的有效性验证 
    int _charbuf;   //检查缓冲区状况,如果⽆缓冲区则不读取 
    int _bufsiz;    //文件的⼤⼩ 
    char *_tmpfname;    //临时文件名
}; 
typedef struct _iobuf FILE;

Linux下FILE信息如下:

c
typedef struct{ 
    short level; //缓冲区"满"或者"空"的程度 
    unsigned flags; //文件状态标志 
    char fd; //文件描述符 
    unsigned char hold; //如⽆缓冲区不读取字符 
    short bsize; //缓冲区的⼤⼩ 
    unsigned char* buffer;//数据缓冲区的位置 
    unsigned ar; //指针,当前的指向 
    unsigned istemp; //临时文件,指⽰器 
    short token; //用于有效性的检查 
}FILE;

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

声明FILE结构体类型的信息包含在头文件“stdio.h”中,一般设置一个指向FILE类型变量的指针变量,然后通过它来引用这些FILE类型变量。通过文件指针就可对它所指的文件进行各种操作。

文件缓冲区

文件缓冲区

ANSI C标准采用“缓冲文件系统”处理数据文件。

所谓缓冲文件系统是指系统⾃动地在内存区为程序中每一个正在使用的文件开辟一个文件缓冲区从内存向磁盘输出数据必须先送到内存中的缓冲区,装满缓冲区后才一起送到磁盘去。

如果从磁盘向计算机读⼊数据,则一次从磁盘文件将一批数据输⼊到内存缓冲区(充满缓冲区),然后再从缓冲区逐个地将数据送到程序数据区(给程序变量) 。

那么文件缓冲区有什么作用呢?

如我们从磁盘里取信息,我们先把读出的数据放在缓冲区,计算机再直接从缓冲区中取数 据,等缓冲区的数据取完后再去磁盘中读取,这样就可以减少磁盘的读写次数,再加上计算 机对缓冲区的操作⼤⼤快于对磁盘的操作,故应用缓冲区可⼤⼤提⾼计算机的运行速度。

磁盘文件的存取

磁盘文件,一般保存在硬盘、U盘等掉电不丢失的磁盘设备中,在需要时调⼊内存;在内存中对文件进行编辑处理后,保存到磁盘中。

程序与磁盘之间交互,不是⽴即完成,系统或程序可根据需要设置缓冲区,以提⾼存取效率。

更新缓冲区

c
#include <stdio.h> 
int fflush(FILE *stream);

功能:更新缓冲区,让缓冲区的数据⽴马写到文件中。

参数

  • stream :文件指针

返回值

  • 成功: 0
  • 失败: -1

Windows和Linux文本文件区别

  • b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
  • Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾

在Windows平台下,以“文本”⽅式打开文件,不加b:

  • 当读取文件的时候,系统会将所有的 "\r\n" 转换成 "\n"
  • 当写⼊文件的时候,系统会将 "\n" 转换成 "\r\n" 写⼊
  • 以"二进制"⽅式打开文件,则读\写都不会进行这样的转换

在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输⼊输出

判断文本文件是Linux格式还是Windows格式

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

int main(int argc, char **args) {
    if (argc < 2) return 0;
    FILE *p = fopen(args[1], "rb");
    if (!p) return 0;
    char a[1024] = {0};
    fgets(a, sizeof(a), p);
    int len = 0;
    while (a[len]) {
        if (a[len] == '\n') {
            if (a[len - 1] == '\r') {
                printf("windows file\n");
            } else {
                printf("linux file\n");
            }
        }
        len++;
    }
    fclose(p);

    system("pause");
    return 0;
}

程序输出:

shell
windows file