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+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;
}