C语言的文件操作:重新理解与演绎
C语言 文件操作
文件概述
一、磁盘文件和设备文件
1、磁盘文件
指一组相关数据的有序集合,通常存储在外部介质(如磁盘)上,使用时才调入内存。
2、设备文件
在操作系统中把每一个与主机相连的输入、输出设备看作是一个文件,把它们的输入、输出等同于对磁盘文件的读和写。
二、磁盘文件的分类
计算机的存储在物理上是二进制的,所以物理上所有的磁盘文件本质上都是一样的:以字节为单位进行顺序存储。
从用户或者操作系统使用的角度(逻辑上)把文件分为:
- 文本文件:基于字符编码的文件
- 二进制文件:基于值编码的文件
三、文本文件和二进制文件
1、文本文件
- 基于字符编码,常见编码有ASCII、UNICODE等
- 一般可以使用文本编辑器直接打开
数5678的以ASCII存储形式(ASCII码)为:
00110101 00110110 00110111 00111000
2、二进制文件
- 基于值编码,自己根据具体应用,指定某个值是什么意思
- 把内存中的数据按其在内存中的存储形式原样输出到磁盘上
数5678的存储形式(二进制码)为:
00010110 00101110
文件打开和关闭
一、文件指针
在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类型变量。通过文件指针就可对它所指的文件进行各种操作。
C语言中有三个特殊的文件指针由系统默认打开,用户无需定义即可直接使用:
- stdin: 标准输入,默认为当前终端(键盘),我们使用的scanf、getchar函数默认从此终端获得数据。
- stdout:标准输出,默认为当前终端(屏幕),我们使用的printf、puts函数默认输出信息到此终端。
- stderr:标准出错,默认为当前终端(屏幕),我们使用的perror函数默认输出信息到此终端。
二、文件打开
1、说明
任何文件使用之前必须打开:
#include <stdio.h> FILE * fopen(const char * filename, const char * mode);
功能:打开文件
参数:
- filename:需要打开的文件名,根据需要加上路径
- mode:打开文件的模式设置
返回值:
- 成功:文件指针
- 失败:NULL
2、第一个参数的几种形式
FILE *fp_passwd = NULL; //相对路径: //打开当前目录passdw文件:源文件(源程序)所在目录 FILE *fp_passwd = fopen("passwd.txt", "r"); //打开当前目录(test)下passwd.txt文件 fp_passwd = fopen(". / test / passwd.txt", "r"); //打开当前目录上一级目录(相对当前目录)passwd.txt文件 fp_passwd = fopen(".. / passwd.txt", "r"); //绝对路径: //打开C盘test目录下一个叫passwd.txt文件 fp_passwd = fopen("c://test//passwd.txt","r");
3、第二个参数的几种形式(打开文件的方式)
注意事项:
- b是二进制模式的意思,b只是在Windows有效,在Linux用r和rb的结果是一样的
- Unix和Linux下所有的文本文件行都是\n结尾,而Windows所有的文本文件行都是\r\n结尾
- 在Windows平台下,以“文本”方式打开文件,不加b:
- 当读取文件的时候,系统会将所有的 "\r\n" 转换成 "\n"
- 当写入文件的时候,系统会将 "\n" 转换成 "\r\n" 写入
- 以"二进制"方式打开文件,则读\写都不会进行这样的转换
- 在Unix/Linux平台下,“文本”与“二进制”模式没有区别,"\r\n" 作为两个字符原样输入输出
打开模式 |
含义 |
r或rb |
以只读方式打开一个文本文件(不创建文件,若文件不存在则报错) |
w或wb |
以写方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a或ab |
以追加方式打开文件,在末尾添加内容,若文件不存在则创建文件 |
r+或rb+ |
以可读、可写的方式打开文件(不创建新文件) |
r+或rb+ |
以可读、可写的方式打开文件(不创建新文件) |
w+或wb+ |
以可读、可写的方式打开文件(如果文件存在则清空文件,文件不存在则创建一个文件) |
a+或ab+ |
以添加方式打开文件,打开文件并在末尾更改文件,若文件不存在则创建文件 |
4、案例
#include <stdio.h> int main(void) { FILE *fp = NULL; // "\\"这样的路径形式,只能在windows使用 // "/"这样的路径形式,windows和linux平台下都可用,建议使用这种 // 路径可以是相对路径,也可是绝对路径 fp = fopen("../test", "w"); //fp = fopen("..\\test", "w"); if (fp == NULL) //返回空,说明打开失败 { //perror()是标准出错打印函数,能打印调用库函数出错原因 perror("open"); return -1; } return 0; }
三、文件关闭
1、说明
任何文件在使用后应该关闭:
- 打开的文件会占用内存资源,如果总是打开不关闭,会消耗很多内存
- 一个进程同时打开的文件数是有限制的,超过最大同时打开文件数,再次调用fopen打开文件会失败
- 如果没有明确的调用fclose关闭打开的文件,那么程序在退出的时候,操作系统会统一关闭。
#include <stdio.h>
int fclose(FILE * stream);
功能:关闭先前fopen()打开的文件。此动作让缓冲区的数据写入文件中,并释放系统所提供的文件资源。
参数:
- stream:文件指针
返回值:
- 成功:0
- 失败:-1
2、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { // 定义文件指针 FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r"); // 判断文件是否打开成功 // 1、找不到文件 // 2、文件权限(读、写、执行) // 3、程序打开文件超出上限 65535 if (fp == NULL) { printf("打开文件失败\n"); return -1; } printf("文件打开成功:%p\n",fp); // 关闭文件指针 fclose(fp); return 0; }
文件字符读写
一、写文件
1、说明
#include <stdio.h> int fputc(int ch, FILE * stream);
功能:将ch转换为unsigned char后写入stream指定的文件中
参数:
- ch:需要写入文件的字符
- stream:文件指针
返回值:
- 成功:成功写入文件的字符
- 失败:返回-1
2、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { // 以写的方式打开文件、如果文件不存在则创建一个新文件、如果文件存在则清空内容 FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "w"); if (!fp) { printf("打开文件失败\n"); return -1; } // 创建字符 // char ch = 'a'; // 字符写入 // 约束:if(value==-1) // fputc(ch, fp); // 通过键盘输入方式存字符 char ch; while (1) { scanf("%c", &ch); // 输入@关闭文件 if (ch == '@') { break; } fputc(ch, fp); } // 关闭文件 fclose(fp); return 0; }
二、文件结尾
1、概述
在C语言中,EOF表示文件结束符(end of file)。在while循环中以EOF作为文件结束标志,这种以EOF作为文件结束标志的文件,必须是文本文件。在文本文件中,数据都是以字符的ASCII代码值的形式存放。我们知道,ASCII代码值的范围是0~127,不可能出现-1,因此可以用EOF作为文件结束标志。
#define EOF (-1)
当把数据以二进制形式存放到文件中时,就会有-1值的出现,因此不能采用EOF作为二进制文件的结束标志。为解决这一个问题,ANSI C提供一个feof函数,用来判断文件是否结束。feof函数既可用以判断二进制文件又可用以判断文本文件。
2、说明
#include <stdio.h>
int feof(FILE * stream);
功能:检测是否读取到了文件结尾。判断的是最后一次“读操作的内容”,不是当前位置内容(上一个内容)。
参数:
- stream:文件指针
返回值:
- 非0值:已经到文件结尾
- 0:没有到文件结尾
3、案例
略
三、读文件
1、说明
#include <stdio.h>
int fgetc(FILE * stream);
功能:从stream指定的文件中读取一个字符
参数:
- stream:文件指针
返回值:
- 成功:返回读取到的字符
- 失败:-1
2、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r"); if (!fp) { printf("打开文件失败\n"); return -1; } // 定义字符 char ch; // 文件字符读取 // 文件默认结尾为 -1 // ch = fgetc(fp); // 不能修改文件指针 // 文件在读取时光标流会自动向下移动 // fp++; // 循环打印 while ((ch = fgetc(fp)) != EOF) { printf("%c", ch); } // 关闭文件 fclose(fp); return 0; }
四、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { FILE* fp1 = fopen("C:/Users/Administrator/Desktop/解密.txt", "r"); FILE* fp2 = fopen("C:/Users/Administrator/Desktop/加密.txt", "w"); if (!fp1 || !fp2)return -1; char ch; while ((ch = fgetc(fp1)) != EOF) { // 加密 ch++; fputc(ch, fp2); } fclose(fp1); fclose(fp2); return 0; }
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { FILE* fp1 = fopen("C:/Users/Administrator/Desktop/加密.txt", "r"); FILE* fp2 = fopen("C:/Users/Administrator/Desktop/解密文件.txt", "w"); if (!fp1 || !fp2)return -1; char ch; while ((ch = fgetc(fp1)) != EOF) { // 解密 ch--; fputc(ch, fp2); } fclose(fp1); fclose(fp2); return 0; }
文件行读写
一、写文件
1、说明
#include <stdio.h> int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
参数:
- str:字符串
- stream:文件指针
返回值:
- 成功:0
- 失败:-1
2、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { // 打开文件 FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "w"); // 判断 if (!fp)return -1; // 创建字符串 // "你想怎\0么滴吧" 遇到\0则停止 // char ch[] = "你想怎么滴吧"; //fputs(ch, fp); // 通过键盘输入获取字符串 char* p = (char*)malloc(sizeof(char) * 1024); while (1) { memset(p, 0, 1024); // 问题:scanf("%s", p);无法接收回车空格 // 方式一、fgets() 可接收空格 // 方式二、scanf("%[^\n]", p); 避免吞噬空格回车 scanf("%[^\n]", p); // 吞噬回车\n getchar(); // 停止输入命令:comm=exit if (!strcmp(p, "comm=exit",9))break; // 追加换行符 strcat(p, "\n"); // 写入字符串 fputs(p, fp); } free(p); fclose(fp); return 0; }
二、读文件
1、说明
#include <stdio.h> char * fgets(char * str, int size, FILE * stream);
功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:
- str:字符串
- size:指定最大读取字符串的长度(size - 1)
- stream:文件指针
返回值:
- 成功:成功读取的字符串
- 读到文件尾或出错: NULL
2、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { // 打开文件 FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r"); // 判断 if (!fp)return -1; // 开辟堆空间 char* p = (char*)malloc(sizeof(char) * 1024); // 初始化内存空间 // memset(p, 0, 5); // 通过堆开放内存,打印文件中100个字节 // 根据光标位置继续向下读取字符串 // fgets(p, 5, fp); // printf("%s", p); // 打印 // feof(文件指针):判断文件是否到结尾 可以判断文本文件也可以判断二进制文件 // 如果到文件结尾返回值为 非0的值 // 如果没到文件结尾返回值为 0的值 while (!feof(fp)) { memset(p, 0, 100); fgets(p, 100, fp); printf("%s", p); } free(p); fclose(fp); return 0; }
三、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> enum opt { add,sub,mlt,dive }; int main(void) { srand((size_t)time(NULL)); // 打开文件 FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "w"); // 判断 if (!fp)return -1; int a, b; char c; // + - * / char * p = (char*)malloc(sizeof(char) * 20); for (int i = 0; i < 100; i++) { a = rand() % 10 + 1; b = rand() % 10 + 1; switch (rand() % 4) { case add:c = '+';break; case sub:c = '-';break; case mlt:c = '*';break; case dive:c = '/';break; } memset(p, 0,20); sprintf(p, "%d%c%d=\n", a, c, b); fputs(p, fp);//fgets(p,size,fp) } free(p); fclose(fp); p = NULL; fp = NULL; return 0; }
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { srand((size_t)time(NULL)); // 打开文件 FILE* fp1 = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r"); FILE* fp2 = fopen("C:/Users/Administrator/Desktop/xsk2.txt", "w"); // 判断 if (!fp1 || !fp2)return -1; // 判断文件结尾 // !feof(fp); // EOF -1 int a, b, sum; char c; char * p = (char*)malloc(sizeof(char) * 20); for (int i = 0; i < 100; i++) { memset(p, 0, 20); // 打印1行内容 fgets(p, 20, fp1); sscanf(p, "%d%c%d=\n", &a, &c, &b); switch (c) { case '+':sum = a + b; break; case '-':sum = a - b; break; case '*':sum = a * b; break; case '/':sum = a / b; break; } memset(p, 0, 20); sprintf(p, "%d%c%d=%d\n", a, c, b, sum); fputs(p, fp2); } free(p); fclose(fp1); fclose(fp2); return 0; }
文件格式化
一、写文件
1、说明
#include <stdio.h> int fprintf(FILE * stream, const char * format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到stream指定的文件中,指定出现字符串结束符 '\0' 为止。
参数:
- stream:已经打开的文件
- format:字符串格式,用法和printf()一样
返回值:
- 成功:实际写入文件的字符个数
- 失败:-1
2、案例
略
二、读文件
1、说明
#include <stdio.h> int fscanf(FILE * stream, const char * format, ...);
功能:从stream指定的文件读取字符串,并根据参数format字符串来转换并格式化数据。
参数:
- stream:已经打开的文件
- format:字符串格式,用法和scanf()一样
返回值:
- 成功:参数数目,成功转换的值的个数
- 失败: - 1
2、案例
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> #include <time.h> int main(void) { // 打开文件 FILE* fp = fopen("C:/Users/Administrator/Desktop/xsk.txt", "r"); // 判断 if (!fp)return -1; char * p = (char*)malloc(sizeof(char) * 100); // 直接格式化取出字符串 // 遇到空格与回车就结束 // fscanf(fp, "%s", p); // printf("%s", p); int a, b, c; // 1+2=3 // 处理表达式 fscanf(fp, "%d+%d=%d", &a,&b,&c); printf("%d\n", a); printf("%d\n", b); printf("%d\n", c); free(p); fclose(fp); return 0; }
三、案例
上一篇: C语言文件读写(1)-文本文件读取操作
下一篇: 文件操作学习指南:探索C语言中的文件处理