文件访问

变量、数组都是存储在内存(RAM)中的,这些数据所占用的内存在程序结束以后会被操作系统回收,其中的数据也就丢失了。因此我们需要将数据保存到外部存储器上(通常为硬盘),便于下次使用。
在一些较为底层的语言里,你可以直接访问硬盘的某个扇区并进行数据读写,但这种方式一般不被推荐,因为这种方式除了效率比较低下外还存在较大的危险性,不恰当的磁盘访问可能会引起严重的故障(例如操作系统崩溃或数据丢失)。
因此我们通常是通过文件来访问磁盘上的数据,文件系统由操作系统管理,程序员通过操作系统间接地访问磁盘上的数据,不恰当的文件访问会被操作系统阻止(例如文件被其他程序占用、或程序没有访问这个文件的权限),这样一来就安全得多,同时操作系统也会采取一些机制来提高文件访问的效率。

使用fopen函数打开或创建文件

要将数据保存到磁盘文件,首先需要创建文件;一个文件如果之前已经创建,则需要打开它(有时是直接覆盖它,取决于你打开它的方式)。在C语言中创建和打开文件都使用一个函数———fopen函数,fopen函数的原型是:
FILE* fopen(const char* filename, const char* mode);
可以看到———fopen函数使用了两个参数,第一个参数是filename,它是一个字符串常量。第二个参数也是一个字符串,它的作用是决定打开这个文件的方式。
以下是fopen函数使用的一个例子:
FILE* fp = fopen("D:\\Data\\Test.txt", "W");
"D:\Data\Test.txt"表示D盘Data目录下的Test.txt文件,之所以是两个斜杠而不是一个,是因为斜杠在字符串中有特殊作用———与其他一些字符组成转义符,例如'\n'表示换行符,'\t'表示制表符,因此单个斜杠在字符串中需要使用斜杠的转义符\来表示。

fopen函数的打开方式

fopen函数的第二个参数用于表示打开文件的方式,可以根据程序的需求决定使用哪种方式;例如,只想读取文件中的数据的话,“只读”权限就够了;既想读取又想写入数据的话,“读写”权限就是必须的了。另外,文件也有不同的类型,按照数据的存储方式可以分为二进制文件和文本文件,它们的操作细节是不同的。在调用 fopen() 函数时,这些信息都必须提供,称为“文件打开方式”。最基本的文件打开方式有以下几种:
fopenDKFS.png
调用 fopen() 函数时必须指明读写权限,但是可以不指明读写方式(此时默认为"t")。
读写权限和读写方式可以组合使用,但是必须将读写方式放在读写权限的中间或者尾部(换句话说,不能将读写方式放在读写权限的开头)。例如:

将读写方式放在读写权限的末尾:"rb"、"wt"、"ab"、"r+b"、"w+t"、"a+t"
将读写方式放在读写权限的中间:"rb+"、"wt+"、"ab+"

整体来说,文件打开方式由 r、w、a、t、b、+ 六个字符拼成,各字符的含义是:

r(read):读
w(write):写
a(append):追加
t(text):文本文件
b(banary):二进制文件
+:读和写

关闭文件

文件一旦使用完毕,应该用fclose()函数把文件关闭,以释放相关资源,避免数据丢失。fclose()的用法为:
int fclose(FILE *fp);
fp 为文件指针。例如:
fclose(fp);
文件正常关闭时,fclose()的返回值为0,如果返回非零值则表示有错误发生。

实例

最后,我们通过一段完整的代码来演示 fopen 函数的用法,这个例子会一行一行地读取文本文件的所有内容:

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

#define N 100

int main() {
    FILE *fp;
    char str[N + 1];

    //判断文件是否打开失败
    if ( (fp = fopen("E:\\DATA\\demo.txt", "rt")) == NULL ) {
        puts("Fail to open file!");
        exit(0);
    }

    //循环读取文件的每一行数据
    while( fgets(str, N, fp) != NULL ) {
        printf("%s", str);
    }

    //操作结束后关闭文件
    fclose(fp);
    return 0;
}

图片处理

#include <stdio.h>
#include <graphics.h>
#include <conio.h>

typedef struct BitMapHeader
{
    long Size;//文件大小
    short Reserved1;//保留字,不考虑
    short Reserved2;//保留字,同上
    long OffBits;//实际位图数据的偏移字节数,即前三个部分长度之和
}BIT_MAP_HEADER;

//信息头BITMAPHEADER,也是一个结构体,其定义如下:
typedef struct BitMapInfo
{
    long   Size;             //指定此结构体的长度,为40   
    long   Width;            //位图宽   
    long   Height;           //位图高   
    short  Planes;           //平面数,为1   
    short  BitCount;         //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32   
    long   Compression;      //压缩方式,可以是0,1,2,其中0表示不压缩   
    long   SizeImage;        //实际位图数据占用的字节数   
    long   XPelsPerMeter;    //X方向分辨率   
    long   YPelsPerMeter;    //Y方向分辨率   
    long   ClrUsed;          //使用的颜色数,如果为0,则表示默认值(2^颜色位数)   
    long   ClrImportant;     //重要颜色数,如果为0,则表示所有颜色都是重要的
} BIT_MAP_INFO;

int main()
{
    FILE* fp = fopen("E:\\DATA\\tx.bmp", "rb");
    char tag[2];
    fread(tag,2, 1, fp);
    if (tag[0] != 'B' || tag[1] != 'M')
    {
        printf("图片读取错误,这不是一个BMP文件");
        return -1;
    }

    BIT_MAP_HEADER bmpHrader;
    fread(&bmpHrader, sizeof(bmpHrader), 1, fp);

    BIT_MAP_INFO bmpInfo;
    fread(&bmpInfo, sizeof(bmpInfo), 1, fp);

    printf("这个图片的宽度是:%d\n", bmpInfo.Width);
    printf("这个图片的高度是:%d\n", bmpInfo.Height);

    initgraph(bmpInfo.Width, bmpInfo.Height);

    unsigned char data[3];

    for (int y = bmpInfo.Height - 1; y >= 0; y--)
    {
        for (int x = bmpInfo.Width - 1; x >= 0; x--)
        {
            fread(data, 3, 1, fp);
            int b = data[0];
            int g = data[1];
            int r = data[2];
            putpixel(x, y, RGB(r,g,b));
        }
    }
    getch();
    closegraph();
    return 0;
}
最后修改:2023 年 10 月 13 日
如果觉得我的文章对你有用,请随意赞赏