知识点回顾
变量、数组、结构体都是存储数据的方式
结构体是一种变量的模板,相对于变量、它可以存储多项数据,相对于数组,结构体中成员的数据类型可以不同。
#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;
}