输入输出 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+c printf("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(模式) //打开文件设定文件路径要读取的文件,指定文件的操作模式r fopen_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(模式) //打开文件设定文件路径要读取的文件,指定文件的操作模式r errno_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); //0 char 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 1000 void 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; }