C语言基础-第八课-文件访问与图片处理
文件访问 变量、数组都是存储在内存(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() 函数时，这些信息都必须提供，称为“文件打开方式”。最基本的文件打开方式有以下几种： 调用 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; }
C语言基础-第六课-自定义函数与字符串处理
scanf函数 #include <stdio.h> int main(void) { int a,b,c; printf("请输入三个数字:\n"); scanf("%d%d%d",&a,&b,&c); //scanf参数 //参数1：用字符串常量表示的输入格式字符串 //参数2：用户输入的数据要送到那里去（地址） printf("a=%d,b=%d,c=%d\n",a,b,c); return 0; } 返回值 scanf("%d %d",&a,&b); scanf函数返回成功读入的数据项数，读入数据时遇到了“文件结束”则返回EOF。 函数返回值为int型。如果a和b都被成功读入，那么scanf的返回值就是2； 如果只有a被成功读入，返回值为1； 如果a和b都未被成功读入，返回值为0； 如果遇到错误或遇到end of file，返回值为EOF。end of file为Ctrl+z 或者Ctrl+d。 关于scanf_s函数 ANSI C中没有scanf_s(),只有scanf()，scanf()在读取时不检查边界，所以可能会造成内存泄露。所以vc++2005/2008中提供了scanf_s(),在最新的VS编译器中也提供了scanf_s()。在调用时，必须提供一个数字以表明最多读取多少位字符。目前最新的c11标准中已经将scanf_s函数“转正”了。 自定义函数 int sayHello() //函数头 { //函数体 printf("Hello,World\n");//函数体 return 0; //函数体 } //函数体 自定义函数要放在主函数main的上面，如果在主函数下面，则需要在主函数上面添加函数声明，以上面代码为例，上面代码的函数声明为 int sayHello(); 注意，不要将自定义函数定义写在main函数中！ 定义一个lengthOfString函数，实现和strlen函数相同的功能 #include <stdio.h> //计算字符串的长度 //在本函数中，没有通过改变字符串的意图 //可以在char*前面加上const限定符，阻止通过该指针影响字符串的内容 //const表示指针自己的值可以修改，但是不允许你通过它改变它所指向位置的值 int lengrhOfString(const char* srting) { int result = 0; while(*string++ != '\0') { result++; } return result; } 字符串的复制 定义一个stringCopy函数 void stringCopy(char* dest, const char* source) { while(*source !='\0') { *dest = *source; dest++; source++; } *dest = '\0'; }
C语言基础-第五课-字符与字符串处理
总结 无论是中文还是其他语言，文字本身是图形符号。 传输和描述图形是一件很麻烦的事； 所以，如果只是要描述和传输字母，传输它们的编码更为方便。 实际上，在计算机的文件中、内存中存储字符时，存储的是它们的编码。 ABC → 65 66 67 在显示字符时，底层驱动（硬件或软件）会自动根据编码取出字库中这个字符的图形数据，并将其绘制在显示设备上。 65 → 显存地址 C语言使用char数据类型存储字符的编码 字符编码中除了常用字符（A-Z,a-z,制表符），还有控制字符（用于控制显示或打印设备的字符） 回车符（13）——控制打印机的打印头移动到最左侧 换行符（10）——控制打印机滚轴滚动一行 Beep（7）——控制打印机或者计算机发出铃声 字符变量——char类型变量其实存放的是一个编码，这个编码是整型值，所以可以直接将整型值赋值给char类型变量。 字符常量——但是，写程序的时候不能记得每一个字符的编码，所以我们可以使用字符常量来表示一个字符，字符常量就是以一对单引号包含的单个字符； C语言不提供用于存储字符串的基础数据类型，如果要存储多个字符，有两种方式： a) 字符串常量 由一对双引号包含的多个字符，称为字符串常量。 程序中字符串常量，编译器会自动根据其中的字符个数为其分配内存，分配的大小是（字符的个数+1），将字符存储进去，并将最后一个字节设置为0，ASCII码值为0的字符约定俗成的被当做字符串结束的标志。 Question:字符串常量的值是什么？ Answer: 字符串常量的值，是内存中存储这个字符串的内存空间的首地址。 b) 单独分配指定大小的内存空间，用以存储可变的字符串。 在使用printf函数时，如果使用%s作为占位符输出字符串，使用的语句是： printf("%s", str); 此时，printf函数会从指针变量str指向的位置开始输出字符 实例 项目一 在下面的程序中的main内添加代码，不使用printf函数显示指针变量str指向的字符串。 #include <stdio.h> int main() { char* str= (char*)"Hello,World"; printf("这个字符串被存储在首地址为%p的内存空间\n", str); return 0; } 1.for循环版 #include <stdio.h> int main() //------------for循环版 { char* str = (char*)"Hail Hdyra!"; for (int x = 0; x <= 999; x++) { char i = *(str + x); if (i == 0) { break; } putchar(i); } return 0; } 2.while循环版 #include <stdio.h> int main() //-------------while循环版 { char* str = (char*)"Hail Hdyra!"; while (*str != 0) { putchar(*str); str++; } return 0; } 项目二 自定义一个函数printString，使这个函数用于显示字符串的每个字符和它所在内存空间的地址 /* ProgramName：定义printString函数用于显示字符串的每个字符和它所在内存空间的地址 Date:2020年3月16日00点22分 Author：KrisWi */ #include <stdio.h> int printString(const char* str); //函数printString的声明 int main() { char* PutStr = (char*)"Hail Hydra!"; printString(PutStr); return 0; } int printString(const char* str) { while (*str != '\0') { putchar(*str); printf("这个字符存储在地址为%p的内存空间里\n", str); str++; } return 0; }