C语言文件流
输入输出 c语言
初步认识
-
文件读写
-
输入流和输出流 input stream output stream
-
buffer 缓冲区
-
输入流的数据首先被暂存到一个叫做缓冲区的内存区域
再探scanf_s
#include <stdio.h>int main(void) {// 1.数的输入int age;printf("输入你的年龄:");scanf_s("%d", &age); // 不需要加buffer 数没有缓冲区的概念 printf("年龄:%d\n", age);// 2.字符串的输入char name[50];printf("输入你的名字:");scanf_s("%49s", name, 50); //scanf_s("%s", name, (unsigned int)sizeof(name));printf("Hello,%s\n", name);// 3.字符的输入char ch;printf("输入字符:");scanf_s("%c", &ch, 1);printf("字符:%c", ch);return 0;}
scanf_s返回值
#include <stdio.h>int main(void) {int number;int result;puts("输入一个整数:");result = scanf_s("%d", &number);if (result == 1) {printf("你输入了有效的整型值:%d", number);}else if (result == EOF) {// End of file 通常为 -1// 专门用来指示文件读取或输入操作已经达到了数据源的末尾// 在终端中如果要把输入的东西撤销掉可以用ctrl+cprintf("An error occurred or end of file was reached");return 1; // 表示程序出现错误的结束输出}else {printf("Invalid input for integer.\n");return 1;}return 0;}
流(stream)
-
文件流:磁盘 用于读取与写入在磁盘上的文件
-
标准I/O流:
stdin:默认连接到键盘,用于程序输入
stdout:默认连接到控制台或者是屏幕,用于程序输入
stderr:默认连接到控制台或者是屏幕,专门输出错误信息和警告,使得其能够被区分开来或者是重定向到不同的目的地
-
管道流:用于进程之间的通信(IPC),允许一个进程输出成为另一个进程的输入
-
内存流:允许你将流与内存缓冲区关联,使得你可以向内存中读写数据,就像操作文件一样
-
网络流:套接字(Sockets)
-
设备流:特殊文件或者是设备 打印机
C语言用FILE* stream来操作
fopen_s,fgetc,fgets,fclose,读取r模式
#include <stdio.h>#include <inttypes.h>int main(void) {FILE* file_stream = NULL; //c语言操作流要使用的char buffer[256]; //创建缓冲区// fopen()打开文件的函数// fopen_s()跟缓冲区相关的函数,微软提供了_s的函数 传递的参数FILE** _Stream(使用的流),char const* _FileName(文件名), char const* _Mode(模式)//打开文件设定文件路径要读取的文件,指定文件的操作模式rfopen_s(&file_stream, "C:\\c_learning\\c_stream.txt", "r"); // \\是转义序列输出\ "r" read读//fgets(buffer, sizeof(buffer), file_stream); 从文件中读取一行while (fgets(buffer, sizeof(buffer), file_stream) != NULL) //停止循环可以用!= NULL {printf("%s", buffer);}fclose(file_stream);return 0;}
文件路径不对,文件不存在, 文件出现了异常,文件没有权限访问如何处理?
引入头文件#include <stdlib.h> #include <errno.h>
对14行进行修改
errno_t err = fopen_s(&file_stream, "C:\\c_learning\\c_stream.txt", "r"); if (err != 0 || file_stream == NULL) {perror("Error opening file");return EXIT_FAILURE;}
当我们将文件路径更改后不会终止程序的输出 会输出Error opening file: No such file or directory
#include <stdio.h>#include <inttypes.h>#include <stdlib.h>#include <errno.h>int main(void) {FILE* file_stream = NULL; //c语言操作流要使用的char buffer[256]; //创建缓冲区// fopen()打开文件的函数// fopen_s()跟缓冲区相关的函数,微软提供了_s的函数 传递的参数FILE** _Stream(使用的流),char const* _FileName(文件名), char const* _Mode(模式)//打开文件设定文件路径要读取的文件,指定文件的操作模式rerrno_t err = fopen_s(&file_stream, "C:\\c_learning\\c_stream.txt", "r"); // \\是转义序列输出\ "r" read读if (err != 0 || file_stream == NULL) {perror("Error opening file");return EXIT_FAILURE;}while (fgets(buffer, sizeof(buffer), file_stream) != NULL) //停止循环可以用!= NULL {printf("%s", buffer);}int ch;// fgetc一个一个字符去获取 返回的是ASCII while ((ch = fgetc(file_stream)) != EOF) //!= EOF也可以让它持续读取{putchar(ch);}fclose(file_stream);return 0;}
结果只会输出一次文本内容,因为file_stream指针把内容读完了,循环读完了,指针指向最后了,再往下什么都没有
如何解决只读一次的问题?
使用rewind()函数
在32行int ch;前面加上
memset(buffer, 0, sizeof(buffer));rewind(file_stream);
fclose()的优化 涉及文件写入就要按照下面的写法规范
if (fclose(file_stream) != 0){perror("Error closing file!");return EXIT_FAILURE;}
fputs、fputc、w模式
#include <stdio.h>#include <stdlib.h>int main(void) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, "C:\\c_learning\\c_stream.txt", "w"); //"w"写入空的文件,若里面有内容,则先前的内容全部销毁if (err != 0 || file_ptr == NULL) {perror("Failed to open file");return EXIT_FAILURE;}// fputc() fputs() fprintf_s() 用与写入fputc('H', file_ptr);fputc('i', file_ptr);fputc('\n', file_ptr);fputs("This is a line written by fputs.\n", file_ptr);fprintf_s(file_ptr, "Numbers:%d, %f, %s\n", 10, 3.14, "End of example"); //格式化写入fclose(file_ptr);puts("File 'txt' has been written successfully");return 0;}
ftell、fseek、rewind、r+
txt内容为上一节输入的内容
#include <stdio.h> #include <stdlib.h>int main(void) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, "C:\\c_learning\\c_stream.txt", "r+"); //r+打开以便读取和写入,文件必须存在if (err != 0 || file_ptr == NULL) {perror("Failed to open file");return EXIT_FAILURE;}long position = ftell(file_ptr);printf("当前位置:%ld\n", position); //0char buffer[100];if ((fgets(buffer, sizeof(buffer), file_ptr)) != NULL) {printf("从文件读取:%s", buffer); //Hi//再次使用ftell获取新位置position = ftell(file_ptr);printf("读取后的新位置:%ld\n", position); //4 0:H 1:i 2:\r 3\n windows中\r\n构成换行,fgets会把转义字符一起获取}//使用fseek函数移动到指定位置fseek(file_ptr, 0, SEEK_SET);printf("使用fseek后移动到文件的开始位置,SEEK_SET宏定义 = 0:%ld\n", ftell(file_ptr)); //0//rewind 重置文件指针到开头rewind(file_ptr);printf("使用rewind函数直接移动到开始位置%ld\n", ftell(file_ptr)); //0//关闭文件fclose(file_ptr);return 0; }
fscanf_s
从流中读取格式化数据
//文件内容 //Hello 1234 5.67 Z#include <stdio.h> #include <stdlib.h>FILE* stream;int main(void) {long l;float fp;char s[81];char c;errno_t err = fopen_s(&stream, "C:\\c_learning\\c_stream.txt", "r");if (err)printf_s("The file fscanf.out was not opened\n");else{if (fscanf_s(stream, "%80s", s, (unsigned)_countof(s)) != 1) { //%s:这是用于读取字符串的格式说明符。它会从输入流里读取连续的非空白字符,直至遇到空白字符(像空格、制表符、换行符等)才会停止。80指最多读取 80 个字符到目标字符串里printf("读取字符串失败!\n");}if (fscanf_s(stream, "%ld", &l) != 1) {printf("读取ld失败\n");}if (fscanf_s(stream, "%f", &fp) != 1) {printf("读取f失败\n");}if (fscanf_s(stream, " %c", &c, 1) != 1) { //不加空格就会读取浮点数后面那个空格,输出一个空格printf("读取c失败\n");}// Output data read:printf("%s\n", s);printf("%ld\n", l);printf("%f\n", fp);printf("%c\n", c);fclose(stream);} }
fprintf
fprintf:格式化向文件里写入东西
//文件内容 //ID:1 //Name:Frank //Temperature : 36.5 //Grade : A#include <stdio.h> #include <stdlib.h>FILE* stream;int main(void) {long l;float fp;char s[81];char c;errno_t err = fopen_s(&stream, "C:\\c_learning\\c_stream.txt", "w+");if (err != 0) {printf_s("无法打开文件进行写入操作\n");return EXIT_FAILURE;}int id = 1;float temperature = 36.5f;char name[] = "Frank";char grade = 'A';fprintf(stream, "ID:%d\n", id);fprintf(stream, "Name:%s\n", name);fprintf(stream, "Temperature:%.1f\n", temperature);fprintf(stream, "Grade:%c\n", grade);fclose(stream);printf("数据写入完成\n");return 0;}
ferror,feof,clearerr
ferror:错误检查 检查文件流是否有错误发生 有错误返回非零值,无错误返回0
feof:错误检查 检查文件的指针是否到达了文件的末尾 到达返回非零值 没到达返回0
clearerr:清除错误 清除与文件流相关的错误标志
#include <stdio.h>#include <stdlib.h>FILE* file_ptr;int main(void){errno_t err = fopen_s(&file_ptr, "C:\\c_learning\\c_stream.txt", "r");if (err != 0) {printf_s("无法打开文件进行写入操作\n");return EXIT_FAILURE;}char buffer[100];while (fgets(buffer, sizeof(buffer), file_ptr)) {printf("%s", buffer);}if (ferror(file_ptr)) {perror("读取文件时候发生了错误");clearerr(file_ptr);}if (feof(file_ptr)) {printf("已经到达文件的末尾\n");}else {printf("文件未达到末尾,可能因为发生了错误");}fclose(file_ptr);return 0;}
抽离读写函数
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>void read_config_safe(const char* filename);int main(void){const char* file_name = "C:\\c_learning\\game_config.txt";read_config_safe(file_name);return 0;}void read_config_safe(const char* filename) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, filename, "r");if (err != 0 ||file_ptr == NULL) {char error_msg[256];strerror_s(error_msg, sizeof(error_msg), errno);fprintf(stderr, "Failed to open config file for reading:%s\n", error_msg);exit(EXIT_FAILURE);}char buffer[256];while (fgets(buffer, sizeof(buffer), file_ptr) != NULL) {printf("%s", buffer);}fclose(file_ptr);}
文本内容:
# Game Configuration File
GraphicsQuality = High FullScreen = true Resolution = 1920 * 1080 AudioVolume = 80 EnableShadows = true; MaxNPCs = 50 Language = English
a模式追加
"a":在文件末尾打开以进行写入(追加),在新数据写入到文件之前不移除文件末尾(EOF)标记。如果文件不存在,则创建文件
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>void append_log_safe(const char* file_name, const char* message);int main(void){const char* log_name = "C:\\c_learning\\log.txt";const char* str = "This is an appending text";append_log_safe(log_name, str);return 0;//确保所有流关闭int numclosed = fcloseall();printf("Number of files closed by _fcloseall : % u\n", numclosed);}void append_log_safe(const char* file_name, const char* message) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, file_name, "a");if (err != 0 || file_ptr == NULL) {char error_message[256];strerror_s(error_message, sizeof(error_message), errno);fprintf(stderr, "Failed to open log file for appending:%s\n", error_message);exit(EXIT_FAILURE);}printf("成功创建并追加!\n");fprintf(file_ptr, "%s\n", message);fclose(file_ptr);}
w模式清空
结合上一节内容,使用w模式清空log的内容
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>void append_log_safe(const char* file_name, const char* message);void clear_log_file(const char* file_name);int main(void){const char* log_file = "C:\\c_learning\\log.txt";const char* str = "This is an appending text";append_log_safe(log_file, str);//使用w模式的函数清空文件clear_log_file(log_file);return 0;//确保所有流关闭int numclosed = fcloseall();printf("Number of files closed by _fcloseall : % u\n", numclosed);}void append_log_safe(const char* file_name, const char* message) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, file_name, "a");if (err != 0 || file_ptr == NULL) {char error_message[256];strerror_s(error_message, sizeof(error_message), errno);fprintf(stderr, "Failed to open log file for appending:%s\n", error_message);exit(EXIT_FAILURE);}printf("成功创建并追加!\n");fprintf(file_ptr, "%s\n", message);fclose(file_ptr);}void clear_log_file(const char* file_name) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, file_name, "w");if (err!= 0 || file_ptr == NULL) {char error_message[256];strerror_s(error_message,sizeof(error_message), errno);fprintf(stderr, "Failed to open log file for clearing:%s\n", error_message);exit(EXIT_FAILURE);}printf("成功清除文件!\n");//文件已经是w模式,不用多加其他内容,文件内容会直接清空fclose(file_ptr);}
修改log r+
c语言中数据的更新用r+模式
r+:打开以便读取和写入,文件必须存在
#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#define BUFFER_SIZE 1000void append_log_safe(const char* file_name, const char* message);errno_t update_log_record_s(const char* file_name, const char* search_str, const char* replace_str);int main(void){const char* log_file = "C:\\c_learning\\frank.log";const char* search_str = "Memory usage exceeds 80% of available memory";const char* replace_str = "[2023-02-14T12:36:00Z] [INFO] Memory usage is back to normal levels";errno_t result = update_log_record_s(log_file, search_str, replace_str);if (result != 0) {char error_msg[256];strerror_s(error_msg, sizeof(error_msg), result);fprintf(stderr,"An error occured:%s\n", error_msg);}else {printf("Record updated successfully");}_fcloseall();return 0;}void append_log_safe(const char* file_name, const char* message) {FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, file_name, "a");if (err != 0 || file_ptr == NULL) {char error_message[256];strerror_s(error_message, sizeof(error_message), errno);fprintf(stderr, "Failed to open log file for appending:%s\n", error_message);exit(EXIT_FAILURE);}printf("成功创建并追加!\n");fprintf(file_ptr, "%s\n", message);fclose(file_ptr);}errno_t update_log_record_s(const char* file_name, const char* search_str, const char* replace_str) {//卫语句if (file_name == NULL || search_str == NULL || replace_str == NULL) {return EINVAL; //返回无效参数错误}FILE* file_ptr = NULL;errno_t err = fopen_s(&file_ptr, file_name, "r+");if (err != 0 || file_ptr == NULL) {char error_msg[256];strerror_s(error_msg, sizeof(error_msg), errno);fprintf(stderr,"Failed to open config file for reading:%s\n", error_msg);exit(EXIT_FAILURE);}char buffer[BUFFER_SIZE];long position = 0;int found = 0;while (fgets(buffer, BUFFER_SIZE, file_ptr) != NULL) {if (strstr(buffer, search_str)!= NULL) {found = 1;//ftell会保存当前位置 记录下来position = ftell(file_ptr) - (long)strlen(buffer) - 1; //减1以确保从行首开始替换break; // 找到第一个匹配项之后,立刻停止}}if (found) {fseek(file_ptr, position, SEEK_SET); //移动找到记录的位置//计算替换文本和源文本的长度差异size_t replace_len = strlen(replace_str);size_t search_len = strlen(search_str);if (replace_len > BUFFER_SIZE - 1 || search_len > BUFFER_SIZE - 1) {fclose(file_ptr);return ERANGE; //返回错误码,表示字符串长度超出范围}//写入新记录,清除所在位置的行数据memset(buffer, ' ', strlen(buffer)- 1); //用空格去填充,保持文件大小不变buffer[strlen(buffer) - 1] = '\n'; //保留换行符fseek(file_ptr, position, SEEK_SET); //重新回到标记行的开始fputs(buffer, file_ptr); //彻底清除原来行的内容fseek(file_ptr, position, SEEK_SET); //重新回到标记行的开始int result = fputs(replace_str, file_ptr); //写入替换的字符if (result == EOF) {fclose(file_ptr);return errno;}}else {fclose(file_ptr);return ENOENT; //返回未找到的匹配项}fclose(file_ptr);return 0;}
fflush()函数(略过)
立刻将缓冲区的数据保存
多线程用
错误日志可以用
bin二进制文件的写入与读取 wb和rb模式
二进制写入:游戏设置保存
#include <stdio.h>#include <stdlib.h>typedef struct GameSettings {float volume;int resolution_x;int resolution_y;int difficulty;} GameSettings;void save_game_settings(const GameSettings* settings, const char* filename);int main() {// fread// fwrite// 读写二进制// 创建实例GameSettings settings = {0.75, 1920, 1080, 2};save_game_settings(&settings, "C:\\c_learning\\game_settings.bin");return 0;}void save_game_settings(const GameSettings* settings, const char* filename) {FILE* file;errno_t err = fopen_s(&file, filename, "wb"); //wb 写入二进制if (err != 0 ||file == NULL) {perror("无法打开文件进行写入操作");return;}fwrite(settings, sizeof(GameSettings), 1, file);fclose(file);}
二进制读取:游戏设置读取
#include <stdio.h>#include <stdlib.h>typedef struct GameSettings {float volume;int resolution_x;int resolution_y;int difficulty;} GameSettings;void load_game_settings(GameSettings* settings, const char* filename);int main() {// fread// fwrite// 读写二进制GameSettings load_settings;load_game_settings(&load_settings, "C:\\c_learning\\game_settings.bin");printf("游戏设置已经加载\n");printf("音量:%f\n分辨率:%dx%d\n难度:%d星\n", load_settings.volume,load_settings.resolution_x,load_settings.resolution_y,load_settings.difficulty);return 0;}void load_game_settings(GameSettings* settings, const char* filename) {FILE* file;errno_t err= fopen_s(&file, filename, "rb");if (err!= 0||file==NULL) {perror("无法打开文件进行读取操作");return;}fread(settings, sizeof(GameSettings), 1, file);fclose(file);}
复制文件
#include <stdio.h>#include <stdlib.h>int main() {FILE* source_file, * target_file;char source_path[] = "C:\\c_learning\\c_stream.txt";char target_path[] = "C:\\c_learning\\copy_stream.txt";char buffer[1024];size_t byte_read;errno_t err = fopen_s(&source_file, source_path, "rb");if (err != 0 ||source_file == NULL) {perror("无法打开源文件");return EXIT_FAILURE;}err = fopen_s(&target_file, target_path, "wb");if (err!= 0 || target_file == NULL) {perror("无法打开目标文件");fclose(source_file);return EXIT_FAILURE;}while ((byte_read = fread(buffer, 1, sizeof(buffer), source_file)) > 0) {fwrite(buffer, 1, byte_read, target_file);}_fcloseall();puts("文件复制完成");return 0;}