C语言的文件操作的使用

知识点回顾

变量、数组、结构体都是存储数据的方式
结构体是一种变量的模板,相对于变量、它可以存储多项数据,相对于数组,结构体中成员的数据类型可以不同。

#include <stdio.h>
#include <stdlib.h>
struct staff
{
    char FullName[64];//一个汉字占用两个字节
    char Gender;//字符型变量可以存储整数 1--男 2--女 char类型范围:-127~128
    char ID[18];
};
/*
除了使用(struct 结构体名 变量名)这种方式声明结构体变量以外,
还可以使用typedef语句来基于结构体声明数据类型
这个语句的作用就是将结构体 Staff 定义为新的数据类型 STAFF
*/
typedef struct staff STAFF;
int main()
{
    //定义完结构体后,就可以基于结构体 声明结构体变量
    STAFF s;
    //struct staff s;
    s.Gender = 1; //为结构体成员变量赋值
    scanf("%s", s.FullName);
    printf("%s\r\n", s.FullName);
    return 0;
}

通过结构体将多个不同类型的数据结构成员整合在一起,可以方便的多项数据进项操作。

文件操作

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

使用fopen()函数

fopen()函数可以打开或创建文件,想要将数据存储到磁盘上,首先我们就需要在磁盘上创建一个文件;如果这个文件已经创建,那么我们就需要打开它即可(可以直接打开,也可以进行覆盖,具体操作取决于你打开文件的方式)。在C语言中创建和打开文件都使用一个函数,fopen()函数,该函数的原型为:

FILE* fopen(const char* filename, const char* mode);
/*
该函数的2个参数都是字符串常量
参数1:要打开或创建文件的具体路径
参数2:打开或创建文件的具体方式
*/
使用fwrite()函数将读取到对应货币的数据写入到指定路径的文件中:
#include <stdio.h>
#include <D:/CR37/Data/libs/BOCRates/BOCRates.h>
#pragma comment(lib, "D:/CR37/Data/libs/BOCRates/BOCRates.lib")
int main()
{
    ExchangeRate r; //定义一个结构体变量
    //使用ReadBOCRatesByCode函数读取指定货币对应的值到 &r 所指向的结构体中
    ReadBOCRatesByCode("USD", &r);
    //以二进制形式打开或创建指定路径下的Rates.txt文件
    FILE* fp = fopen("D:\\CR37\\Data\\Rates.txt", "wb");
    //做判断
    if (fp == NULL)
    {
        printf("文件打开失败!\r\n");
    }
    else
    {
        printf("文件打开成功,文件的信息存储在一个结构体中,指针fp指向了这个结构体\r\n");
        //将内存中&r地址中的数据值 读取sizeof(ExchangeRate)大小的字节 保存到fp指向的文件内
        fwrite(&r, sizeof(ExchangeRate), 1, fp);
        //使用后要记得关闭文件
        fclose(fp);
    }
    return 0;
}

使用fread()函数将指定路径的文件中的数据读出来:

#include <stdio.h>
#include <D:/CR37/Data/libs/BOCRates/BOCRates.h>
#pragma comment(lib, "D:/CR37/Data/libs/BOCRates/BOCRates.lib")
int main()
{
    //定义一个结构体变量
    ExchangeRate r;
    //以只读的方式打开对应路径下的文件
    FILE* fp = fopen("D:\\CR37\\Data\\Rates.txt", "r");
    //从fp 指向的位置 读取sizeof(ExchangeRate)字节的数据 送到&r所指向的位置
    fread(&r, sizeof(ExchangeRate), 1, fp);
    //打印对应的值
    printf("%s\t", r.CurrencyCode);
    printf("%s\t", r.CurrencyName);
    printf("%s\t", r.PublishTime);
    printf("%f\t", r.BuyingRate);
    printf("%f\t", r.SellingRate);
    printf("%f\t", r.CashBuyingRate);
    printf("%f\t", r.CashSellingRate);
    printf("%f\t\n", r.MiddleRate);
    fclose(fp);
    return 0;
}

程序运行的结果:

通过自定义函数实现和main()中相同的功能

避免main()函数中的语句过于繁琐,更具有模块化,以及程序的可修改性,所以我们需要进行自定义函数,实现文件写入和读取操作等功能:
1、写入(保存)函数:int SaveRates(ExchangeRate r);
2、读取函数:void LoadRatesFromFile(ExchangeRate* r);
3、显示数据:void DisplayRates(ExchangeRate r);
Visual 1:

#include <stdio.h>
#include <D:/CR37/Data/libs/BOCRates/BOCRates.h>
#pragma comment(lib, "D:/CR37/Data/libs/BOCRates/BOCRates.lib")
/*
在联网的基础上将从API上读取指定货币的数据保存到磁盘上
在保存数据时,只需要将结构体变量的值传递进去就可以了,此时无需使用指针
*/
int SaveRates(ExchangeRate r)
{
     FILE* fp = fopen("D:\\CR37\\Data\\Rates.txt", "wb");
     if (fp == NULL)
    {
        printf("文件打开失败!\r\n");
        return -1;
    }
    else
    {
        printf("文件打开成功!文件的信息存储在一个结构体中,指针指向了这个结构体\r\n");
        fwrite(&r, sizeof(ExchangeRate), 1, fp);
        fclose(fp);
        return 0;
    }
}
/*
这里传递结构体变量指针是因为要从磁盘中读取已保存的数据
*/
void LoadRatesFromFile(ExchangeRate* r)
{
    FILE* fp = fopen("D:\\CR37\\Data\\Rates.txt", "r");
    fread(r, sizeof(ExchangeRate), 1, fp);
    fclose(fp);
}
//输出结构体成员对应的数值
void DispalyRates(ExchangeRate r)
{
    printf("%s\t", r.CurrencyCode);
    printf("%s\t", r.CurrencyName);
    printf("%s\t", r.PublishTime);
    printf("%f\t", r.BuyingRate);
    printf("%f\t", r.SellingRate);
    printf("%f\t", r.CashBuyingRate);
    printf("%f\t", r.CashSellingRate);
    printf("%f\t\n", r.MiddleRate);
}
int main()
{
    //声明一个结构体变量
    ExchangeRate r;
    //调用ReadBOCRatesCode()函数,获取指定货币的数据
    ReadBOCRatesByCode("JPY", &r);
    //将其获取指定货币的数据保存到指定路径的文件内
    SaveRates(r);
    
    //调用LoadRatesFromFile()函数将指定路径的文件内的数据读取出来到对应的结构体成员中
    LoadRatesFromFile(&r);
    //调用DispalyRates()函数,打印对应的结构体成员的数值
    DispalyRates(r);

    return 0;
}

Visual 2:

#include <stdio.h>
#include <D:/CR37/Data/libs/BOCRates/BOCRates.h>
#pragma comment(lib, "D:/CR37/Data/libs/BOCRates/BOCRates.lib")
/*
在联网的基础上将从API上读取指定货币的数据保存到磁盘上
在保存数据时,只需要将结构体变量的值传递进去就可以了,此时无需使用指针
*/
int SaveRates(ExchangeRate r)
{
     FILE* fp = fopen("D:\\CR37\\Data\\Rates.txt", "wb");
     if (fp == NULL)
    {
        printf("文件打开失败!\r\n");
        return -1;
    }
    else
    {
        printf("文件打开成功!文件的信息存储在一个结构体中,指针指向了这个结构体\r\n");
        fwrite(&r, sizeof(ExchangeRate), 1, fp);
        fclose(fp);
        return 0;
    }
}
/*
这里传递结构体变量指针是因为要从磁盘中读取已保存的数据
*/
void LoadRatesFromFile(ExchangeRate* r)
{
    FILE* fp = fopen("D:\\CR37\\Data\\Rates.txt", "r");
    fread(r, sizeof(ExchangeRate), 1, fp);
    fclose(fp);
    printf("\r\n");
}
//输出结构体成员对应的数值
void DispalyRates(ExchangeRate r)
{
    printf("%s\t", r.CurrencyCode);
    printf("%s\t", r.CurrencyName);
    printf("%s\t", r.PublishTime);
    printf("%f\t", r.BuyingRate);
    printf("%f\t", r.SellingRate);
    printf("%f\t", r.CashBuyingRate);
    printf("%f\t", r.CashSellingRate);
    printf("%f\t\r\n", r.MiddleRate);
}
int main()
{
     //声明一个结构体变量
    ExchangeRate rates;
    if (ReadBOCRatesByCode("JPY", &rates) == 1)
    {
        //条件为真,将读取到的对应数值保存到指定路径文件内
        SaveRates(rates);
    }
    else
    {
        //如果上面的条件失败,就把本地保存的数读取出来
        LoadRatesFromFile(&rates);
    }
    DispalyRates(rates);
    return 0;
    }

程序运行结果:

使用文件操作打印BMP格式的图片

说明

现在使用的图片主要为位图和矢量图:
矢量图形:是计算机图形学中用点、直线或者多边形等基于数学方程的几何图元表示图像。矢量图形与使用像素表示图像的位图不同。
位图(Bitmap):又称栅格图(英语:Raster graphics)或点阵图,是使用像素阵列(Pixel-array/Dot-matrix点阵)来表示的图像。

转换.bmp格式

需要提前将.png、.jpg等格式的图片通过画图工具转换成.bmp,转换的文件类型需要为24位位图,如下图所示:

位图(.bmp格式)图片的字节数计算方法:

使用WinHex工具以十六进制的方式查看.bmp图,可以看出第一、二字节所表示的字符分别为:'B'、'M'

代码实现
Visual 1:读取图片信息
#include <stdio.h>
int main()
{
    FILE* fp = fopen("D:\\CR37\\Data\\imgs\\computer1.bmp","rb");
    char tag[2];//定义一个字符数组
    fread(tag, 2, 1, fp);//字符数组的名称可以隐式的转换成数组的首地址,参数1无需添 加 & 运算符
    if (tag[0] != 'B' || tag[1] != 'M')
    {
        printf("这张图片不是一张\".bmp\"图片!\r\n");
        return -1;
    }
    else
    {
        printf("这张图片是一张\".bmp\"图片!\r\n");
    }
    //输出图片文件的字节大小
    //BMPd文件的第3~6字节保存了图片的字节数
    int filesize = 0;
    fread(&filesize, 4, 1, fp);
    printf("这张图片的字节大小为:%d 字节\r\n", filesize);
    fclose(fp);
    return 0;
}

程序运行结果:

BMP文件头信息头的结构体定义:

//header C结构体
typedef struct {
   unsigned short int type; //文件格式 占用2Bytes,必须为"BM"
   unsigned int size;       //文件大小,占用4字节
   unsigned short int reserved1, reserved2; //保留区 默认设置为0
   unsigned int offset;//文件头到图像数据信息偏移字节数
} HEADER;
 
//infoheader 结构体
typedef struct {
   unsigned int size;   //文件部分size大小
   int width;   //图像宽
   int height;    //图像高 
   unsigned short int planes;   // 位面数 总为1
   unsigned short int bits; //素位深 
   unsigned int compression;    //缩类型 
   unsigned int imagesize;  //像大小(字节)
   int xresolution,yresolution; //率(水平,垂直
   unsigned int ncolours;   //所用颜色索引数目 
   unsigned int importantcolours;   //像显示有重要影响的颜色索引数
} INFOHEADER;
Visual 2:定于结构体,获取BMP图片的一些信息
#include <stdio.h>
#include <graphics.h>
#include <conio.h>
/*
BMP头文件54字节的结构体声明
使用typedef()为struct BitMapHeader结构体定义别名:BIT_MAP_HEADER
*/
typedef struct BitMapHeader
{
    long Size;//文件大小
    short Reserved1;//保留字,不考虑
    short Reserved2;//保留字,同上
    long OffBits;//实际位图数据的偏移字节数,即前三个部分长度之和
}BIT_MAP_HEADER;

/*
BMP信息头,也是一个结构体,其定义了BMP图片的具体参数信息
使用typedef()为struct BitMapInfo结构体定义别名:BIT_MAP_INFO
*/
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("D:\\CR37\\Data\\imgs\\computer1.bmp","rb");
    char tag[2];//定义一个字符数组
    fread(tag, 2, 1, fp);//字符数组的名称可以隐式的转换成数组的首地址,参数1无需添 加 & 运算符
    if (tag[0] != 'B' || tag[1] != 'M')
    {
        printf("这张图片不是一张\".bmp\"图片!\r\n");
        return -1;
    }
    else
    {
        printf("这张图片是一张\".bmp\"图片!\r\n");
        //为BMP头文件结构体声明变量
        BIT_MAP_HEADER bmpHeader;
        fread(&bmpHeader, sizeof(bmpHeader), 1, fp);
        
        //为BMP信息头结构体声明变量
        BIT_MAP_INFO bmpInfo;
        fread(&bmpInfo, sizeof(bmpInfo), 1, fp);
        
        printf("这张BMP图片的宽度为:%d\r\n", bmpInfo.Width);
        printf("这张BMP图片的高度为:%d\r\n", bmpInfo.height);
    }
    fclose(fp);
    return 0;
}

程序运行结果:

根据上面使用结构体获取的BMP图片信息,使用initgraph()函数就可以初始化一个对应大小像素的窗口

    initgraph(bmpInfo.Width, bmpInfo.Height);
    _getch();

运行结果如下:

给初始化的绘图窗口填充颜色:

    initgraph(bmpInfo.Width, bmpInfo.Height);
    for (int x = 0; x <= bmpInfo.Width; x++)
    {
        for (int y = 0; y <= bmpInfo.Height; y++)
        {
            putpixel(x, y, RGB(0, 0, 255));
        }
    }

程序运行结果:

从fp读取对应像素点的RGB数值:

    initgraph(bmpInfo.Width, bmpInfo.Height);
    //位图每个像素点的RGB值占用3个字节
    char data[3];//存储像素点的RGB值
    for (int x = 0; x <= bmpInfo.Width; x++)
    {
        for (int y = 0; y <= bmpInfo.Height; y++)
        {
            //从fp读取数据到data里
            fread(data, 3, 1, fp);
            //字符型数据可以转换成整型
            int r = data[0];
            int g = data[1];
            int b = data[2];
            putpixel(i, j, RGB(r, g, b));
        }
    }

程序运行结果(可以看出结果并不是我们想要的,多以找bug):

由于位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。所以需要更改循环条件:

    initgraph(bmpInfo.Width, bmpInfo.Height);
    //位图每个像素点的RGB值占用3个字节
    char data[3];//存储像素点的RGB值
    for (int y = bmpInfo.Height - 1; y >= 0; y--)
    {
        for (int x =0 ; x <= bmpInfo.Width -1; x++)
        {
            //从fp读取数据到data里
            fread(data, 3, 1, fp);
            //字符型数据可以转换成整型
            int r = data[0];
            int g = data[1];
            int b = data[2];
            putpixel(i, j, RGB(r, g, b));
        }
    }

程序运行结果:

还可以修改for()循环里的条件:

    for (int y = bmpInfo.Height - 1; y >= 0; y--)
    {
        //修改这里的循环条件
        for (int x =bmpInfo.Width -1 ; x >= 0; x--)
        {
            //从fp读取数据到data里
            fread(data, 3, 1, fp);
            //字符型数据可以转换成整型
            int r = data[0];
            int g = data[1];
            int b = data[2];
            putpixel(i, j, RGB(r, g, b));
        }
    }

程序运行结果:

Visual 3:完整代码
#include <stdio.h>
#include <graphics.h>
#include <conio.h>
/*
BMP头文件54字节的结构体声明
使用typedef()为struct BitMapHeader结构体定义别名:BIT_MAP_HEADER
*/
typedef struct BitMapHeader
{
    long Size;//文件大小
    short Reserved1;//保留字,不考虑
    short Reserved2;//保留字,同上
    long OffBits;//实际位图数据的偏移字节数,即前三个部分长度之和
}BIT_MAP_HEADER;

/*
BMP信息头,也是一个结构体,其定义了BMP图片的具体参数信息
使用typedef()为struct BitMapInfo结构体定义别名:BIT_MAP_INFO
*/
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("D:\\CR37\\Data\\imgs\\computer1.bmp","rb");
    char tag[2];//定义一个字符数组
    fread(tag, 2, 1, fp);//字符数组的名称可以隐式的转换成数组的首地址,参数1无需添 加 & 运算符
    if (tag[0] != 'B' || tag[1] != 'M')
    {
        printf("这张图片不是一张\".bmp\"图片!\r\n");
        return -1;
    }
    else
    {
        printf("这张图片是一张\".bmp\"图片!\r\n");
        //为BMP头文件结构体声明变量
        BIT_MAP_HEADER bmpHeader;
        fread(&bmpHeader, sizeof(bmpHeader), 1, fp);
        
        //为BMP信息头结构体声明变量
        BIT_MAP_INFO bmpInfo;
        fread(&bmpInfo, sizeof(bmpInfo), 1, fp);
        
         initgraph(bmpInfo.Width, bmpInfo.Height);
        //位图每个像素点的RGB值占用3个字节
        char data[3];//存储像素点的RGB值
        for (int y = bmpInfo.Height - 1; y >= 0; y--)
        {
            for (int x =0 ; x <= bmpInfo.Width -1; x++)
            {
                //从fp读取数据到data里
                fread(data, 3, 1, fp);
                //字符型数据可以转换成整型
                int r = data[0];
                int g = data[1];
                int b = data[2];
                putpixel(i, j, RGB(r, g, b));
                _getch();
                closegraph();
            }
        }
    }
    fclose(fp);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ileemi/p/12523893.html