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)

  1. 文件流:磁盘 用于读取与写入在磁盘上的文件

  2. 标准I/O流:

    stdin:默认连接到键盘,用于程序输入

    stdout:默认连接到控制台或者是屏幕,用于程序输入

    stderr:默认连接到控制台或者是屏幕,专门输出错误信息和警告,使得其能够被区分开来或者是重定向到不同的目的地

  3. 管道流:用于进程之间的通信(IPC),允许一个进程输出成为另一个进程的输入

  4. 内存流:允许你将流与内存缓冲区关联,使得你可以向内存中读写数据,就像操作文件一样

  5. 网络流:套接字(Sockets)

  6. 设备流:特殊文件或者是设备 打印机

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;
}
posted @ 2025-05-10 12:21  会编程的啊啵  阅读(24)  评论(0)    收藏  举报