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;
 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值