FILE* stream
这种定义方式包含了各种各样的流。流是一种用于在程序和外部设备(如文件、控制台、网络等)之间进行数据传输的抽象概念。stdin
:默认连接到键盘,用于程序输入,通常与scanf_s
等函数配合使用,接收用户从键盘输入的数据。stdout
:默认连接到控制台或者是屏幕,用于程序输出,常见的使用场景是与printf
等函数一起,将程序计算结果或提示信息显示在屏幕上,让用户能够看到程序的运行状态和结果。stderr
:默认也连接到控制台或者屏幕,专门用于输出错误信息和警告。这样可以使错误信息和正常的输出信息区分开来,方便用户查看和处理程序中的错误情况。fopen_s
函数
errno_t fopen_s(FILE **stream, const char *filename, const char *mode);
stream
:指向一个指针的指针,用于接收打开文件后返回的文件指针。filename
:指定要打开的文件的名称和路径。可以是相对路径或绝对路径。mode
:指定文件的打开模式,如r
(只读)、w
(只写)、a
(追加)等。errno
获取错误信息。fclose
函数
int fclose(FILE *stream);
fgets
函数
num - 1
个字符,并将其存储到字符数组str
中,直到遇到换行符\n
或者文件结束符EOF
为止。读取完成后,会在字符串末尾自动添加字符串结束符'\0'
。char *fgets(char *str, int num, FILE *stream);
str
:指向用于存储读取数据的字符数组的指针。num
:指定要读取的最大字符数,包括字符串结束符'\0'
。stream
:指向要读取数据的文件流的指针。fputs
函数
int fputs(const char *str, FILE *stream);
str
:指向要写入文件的字符串的指针。stream
:指向要写入数据的文件流的指针。fprintf_s
函数
int fprintf_s(FILE *stream, const char *format,...);
stream
:指向要写入数据的文件流的指针。format
:指定数据的输出格式,类似于printf
函数中的格式字符串。...
:根据格式字符串指定的格式,依次列出要写入的数据。fscanf_s
函数
int fscanf_s(FILE *stream, const char *format,...);
stream
:指向要读取数据的文件流的指针。format
:指定数据的输入格式,类似于scanf
函数中的格式字符串。...
:指向用于存储读取数据的变量的地址。fread
函数
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr
:指向用于存储读取数据的内存块的指针。该内存块的大小至少应该为size * nmemb
字节,以确保能够容纳读取的数据。size
:每个数据项的大小(以字节为单位)。例如,如果要读取整数,size
可以设置为sizeof(int)
。nmemb
:要读取的数据项的数量。函数将尝试读取nmemb
个大小为size
字节的数据项。stream
:指向要从中读取数据的文件流。nmemb
。fwrite
函数
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
ptr
:指向要写入的数据的指针。该指针指向的数据块将被写入到文件流中。size
:每个数据项的大小(以字节为单位)。例如,如果要写入整数,size
通常设置为sizeof(int)
;若写入字符,size
则为sizeof(char)
。nmemb
:要写入的数据项的数量。函数会尝试将nmemb
个大小为size
字节的数据项写入文件流。stream
:指向FILE
对象的指针,表示数据要写入的目标文件流。nmemb
。fseek
函数
int fseek(FILE *stream, long int offset, int whence);
stream
:指向要操作的文件流。offset
:指定要移动的字节数。可以是正数、负数或 0。whence
SEEK_SET
:从文件开头开始计算偏移量。SEEK_CUR
:从当前文件指针的位置开始计算偏移量。SEEK_END
:从文件末尾开始计算偏移量。ftell
函数
long int ftell(FILE *stream);
-1L
。ferror
函数
int ferror(FILE *stream);
feof
函数
int feof(FILE *stream);
clearerr
函数
void clearerr(FILE *stream);
scanf_s
的返回值示例 int main(void) {
int number;
int result;
puts("Enter an integer:");
result = scanf_s("%d", &number);
if (result == 1) {
printf("You entered thr integer: %d", number);
} else if (result == EOF) {
// End of file -1
// 专门用来指示文件读取或输入操作已经到达了数据源的末尾
printf("An error occurred or end of file was reached.\n");
return 1; // 直接退出函数
} else {
printf("Invalid input for integer.\n");
return 1;
}
return 0;
}
#include
#include
#include
#include
int main(void) {
FILE *fileStream = NULL;
char buffer[1023] = { 0 };
// fopen_s()
// 打开文件,设定文件路径要读取的文件,设定文件的操作模式
errno_t err = fopen_s(&fileStream, "C:\\Users\\yangy\\Desktop\\Hello!.txt", "r");
// 这里对返回值进行处理,!= 0说明读取失败或者是读取的流是一个NULL
if (err!= 0 || fileStream == NULL) {
// 这是专门用来错误处理的函数,正确地反馈错误
perror("Error opening file");
return EXIT_FAILURE;
}
// 需要对fopen进行处理,可能会出现文件路径不对,文件不存在,文件出现了异常,文件没有权限访问
// 令fgets!= NULL即可一直读取下去
while (fgets(buffer, sizeof(buffer), fileStream)!= NULL) {
printf("%s", buffer);
}
// 之前用的字符串buffer此时里面是有东西的,之后我们不再使用了,就得确保他是空的,不然是一件非常不文明的事情
memset(buffer, 0, sizeof(buffer));
// 如果需要读取两次的话,第一次读的文件末尾是没有换行符的,需要手动加
printf("\n");
// 这个函数不管多长都能直接定位到文件开头
fseek(fileStream, 0, SEEK_SET);
// 也可以一个字符的读,一个字一个字往出蹦,不需要缓冲区但是比较慢
int ch = 0;
while ((ch = fgetc(fileStream))!= EOF) {
putchar(ch);
}
// 最后一定要关闭文件流,关闭也可能出现问题,需要对fclose进行处理
if (fclose(fileStream)!= 0) {
perror("Error closing file");
return EXIT_FAILURE;
}
return 0;
}
fseek
功能描述示例 // 示例1:移动到文件开头
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
exit(EXIT_FAILURE);
}
// 将指针移动到文件开头(等价于rewind(fp))
fseek(fp, 0, SEEK_SET);
// 示例2:跳过前N字节
// 跳过前100字节(从开头向后移动)
fseek(fp, 100, SEEK_SET);
// 读取后续内容
char buffer[1024];
fread(buffer, sizeof(char), 1024, fp);
// 示例3:从当前位置移动
// 假设当前指针在位置200
// 向后移动50字节(正偏移)
fseek(fp, 50, SEEK_CUR); // 新位置 = 250
// 向前移动30字节(负偏移)
fseek(fp, -30, SEEK_CUR); // 新位置 = 220
// 示例4:移动到文件末尾
// 移动到文件末尾
fseek(fp, 0, SEEK_END); // 用于追加数据或计算文件大小
// 在末尾追加数据
fputs("追加的内容", fp);
// 示例5:读取倒数第N字节
// 读取文件的最后10字节
fseek(fp, -10, SEEK_END); // 从末尾向前移动10字节
char last10[11];
fread(last10, sizeof(char), 10, fp);
last10[10] = '\0'; // 添加字符串结束符
printf("最后10字节: %s\n", last10);
// 示例6:修改文件中间内容
FILE *fp = fopen("data.bin", "r+"); // 读写模式
if (fp == NULL)
exit(EXIT_FAILURE);
fseek(fp, 49, SEEK_SET); // 移动到第50字节(索引从0开始)
fputc('X', fp); // 修改该字节
fclose(fp);
// 示例7:动态计算文件大小
FILE *fp = fopen("data.bin", "rb");
if (fp == NULL)
exit(EXIT_FAILURE);
fseek(fp, 0, SEEK_END); // 移动到文件末尾
long file_size = ftell(fp); // 获取文件大小(字节数)
rewind(fp); // 回到开头
printf("文件大小: %ld字节\n", file_size);
// 示例8:复杂偏移组合
// 假设文件指针初始位置为100
fseek(fp, 100, SEEK_SET);
// 从当前位置向前移动50字节
fseek(fp, 50, SEEK_CUR); // 新位置 = 150
// 从文件末尾向前移动200字节
fseek(fp, -200, SEEK_END); // 适用于大文件末尾操作
fwrite
和fread
示例 // 示例1:写入整数数组到二进制文件
#include
#define ARRAY_SIZE 5
int main() {
FILE *file = fopen("data.bin", "wb");
if (file == NULL) {
perror("无法打开文件");
return 1;
}
int numbers[ARRAY_SIZE] = {1, 2, 3, 4, 5};
size_t items_written = fwrite(numbers, sizeof(int), ARRAY_SIZE, file);
if (items_written < ARRAY_SIZE) {
if (ferror(file)) {
perror("写入文件时发生错误");
}
} else {
printf("成功写入 %zu个整数到文件。\n", items", items_written);
}
fclose(file);
return 0;
}
// 示例2:写入字符串到文件
int main() {
FILE *file = fopen("text.txt", "w");
if (file == NULL) {
perror("无法打开文件");
return 1;
}
char str[] = "Hello, World!";
size_t items_written = fwrite(str, sizeof(char), sizeof(str), file);
if (items_written < sizeof(str)) {
if (ferror(file)) {
perror("写入文件时发生错误");
}
} else {
printf("成功写入字符串到文件。\n");
}
fclose(file);
return 0;
}
#include
#include
#include
int main(void) {
FILE *source_file = NULL;
FILE *target_file = NULL;
char source_path[] = "C:\\Users\\yangy\\Desktop\\abc.log";
char target_path[] = "C:\\Users\\yangy\\Desktop\\log.txt";
errno_t sourerr = fopen_s(&source_file, source_path, "rb");
if (sourerr!= 0 || source_file == NULL) {
perror("Error to open source file");
return EXIT_FAILURE;
}
errno_t tarerror = fopen_s(&target_file, target_path, "wb");
if (tarerror!= 0 || target_file == NULL) {
perror("Error to open target file");
return EXIT_FAILURE;
}
size_t byte_read = 0;
char buffer[256] = { 0 };
while ((byte_read = fread(buffer, 1, sizeof(buffer), source_file)) > 0) {
fwrite(buffer, 1, byte_read, target_file);
}
_fcloseall();
printf("succussfully");
return 0;
}
#include
#include
#include
typedef enum {
ESAY,
MIDDLE,
HARD
} Difficulty;
typedef struct {
float volumn;
int resolution_x;
int resolution_y;
Difficulty Difficulty;
} Settings;
// 读取设置
void load_game_settings(const Settings *settings, const char *filename);
// 保存设置
void save_game_settings(const Settings *settings, const char *filename);
int main(void) {
Settings settings = { 0.75, 1920, 1080, MIDDLE };
save_game_settings(&settings, "C:\\Users\\yangy\\Desktop\\log.txt");
Settings load_setting = { 0 };
load_game_settings(&load_setting, "C:\\Users\\yangy\\Desktop\\log.txt");
printf("volunm : %f\nresolution_x : %d\nresolution_y : %d\ndifficulty : %d", load_setting.volumn, load_setting.resolution_x, load_setting.resolution_y, load_setting.Difficulty);
return 0;
}
void save_game_settings(const Settings *settings, const char *filename) {
FILE *file_stream = NULL;
errno_t err = fopen_s(&file_stream, filename, "wb");
if (err!= 0 || file_stream == NULL) {
char config_msg[256] = { 0 };
strerror_s(config_msg, sizeof(config_msg), errno);
fprintf(stderr, "Error to open the file: %s", config_msg);
exit(EXIT_FAILURE);
}
// 总共有四个参数,分别是指向缓冲区的指针,所读取数据的大小,每次读取的数目
fwrite(settings, sizeof(Settings), 1, file_stream);
fclose(file_stream);
}
void load_game_settings(const Settings *settings, const char *filename) {
FILE *file_stream = NULL;
errno_t err = fopen_s(&file_stream, filename, "rb");
if (err!= 0 || file_stream == NULL) {
char config_msg[256] = { 0 };
strerror_s(config_msg, sizeof(config_msg), errno);
fprintf(stderr, "Error to open the file: %s", config_msg);
exit(EXIT_FAILURE);
}
// 总共有四个参数,分别是指向缓冲区的指针,所读取数据的大小,每次读取的数目
fread(settings, sizeof(Settings), 1, file_stream);
fclose(file_stream);
}
fopen_s
的不同模式模式 | 描述 | 文件存在 | 文件不存在 | 文件指针位置 | 操作权限 |
---|---|---|---|---|---|
r |
只读 | 打开文件 | 报错 | 开头 | 只能读 |
w |
只写 | 清空 | 创建文件 | 开头 | 只能写 |
a |
追加 | 打开文件 | 创建文件 | 末尾 | 只能写 |
模式 | 描述 | 文件存在 | 文件不存在 | 文件指针位置 | 操作权限 |
---|---|---|---|---|---|
r+ |
读写(更新模式) | 打开文件 | 报错 | 开头 | 可读可写 |
w+ |
读写(清空并创建) | 清空 | 创建文件 | 开头 | 可读可写 |
a+ |
读写(追加模式) | 打开文件 | 创建文件 | 末尾 | 可读可写 |
注 |
a+
模式下,写入总是追加到末尾,但读取可以定位到任意位置(需先调用fseek
调整指针)。r+
和w+
的主要区别:r+
不删除原内容,w+
会清空原内容。在模式后添加b
(如rb
,wb+
),用于处理二进制文件。
\r\n
转为\n
)。scanf
的格式控制符处理%s
读取字符串时当使用%s
格式化控制符时,scanf
不会读取空格、制表符(\t
)和换行符(\n
),它会将这些字符视为字符串的分隔符。一旦遇到这些空白字符,scanf
就会停止读取,并在字符串末尾自动添加字符串结束符'\0'
。
%c
读取字符时当使用%c
格式化控制符时,scanf
会读取包括空格、制表符和换行符在内的任意字符。也就是说,空格对于%c
来说就是一个普通的字符。
%d
、%f
等)读取数值时对于读取整数(%d
)、浮点数(%f
)等数值类型的格式控制符,scanf
会自动跳过前导的空白字符(空格、制表符、换行符),直到遇到有效的数字字符才开始读取。
fseek
偏移量与实际字节数不一致。from micro_frank