目录
[一. sprintf函数和sscanf函数](#一. sprintf函数和sscanf函数)
[1.1 sprintf 函数:将格式化数据写入字符串](#1.1 sprintf 函数:将格式化数据写入字符串)
[1.2 sscanf 函数:从字符串中格式化读取数据](#1.2 sscanf 函数:从字符串中格式化读取数据)
[二. 文件的随机读写](#二. 文件的随机读写)
[2.1 fseek 函数:移动文件读写指针](#2.1 fseek 函数:移动文件读写指针)
[2.2 ftell 函数:获取当前指针位置](#2.2 ftell 函数:获取当前指针位置)
[2.3 rewind 函数:将指针重置到文件开头](#2.3 rewind 函数:将指针重置到文件开头)
[三. 文件缓冲区](#三. 文件缓冲区)
[3.1 缓冲区的作用](#3.1 缓冲区的作用)
[3.2 缓冲区的分类(按刷新策略)](#3.2 缓冲区的分类(按刷新策略))
[3.3 缓冲区的操作函数](#3.3 缓冲区的操作函数)
一. sprintf函数和sscanf函数
在 C 语言中,sprintf
和 sscanf
是用于字符串格式化处理 的函数,与 printf
/scanf
类似,但操作对象不是标准流或文件流,而是字符数组(字符串),常用于字符串的拼接、解析和格式转换。
1.1 sprintf
函数:将格式化数据写入字符串
函数原型
cpp
#include <stdio.h>
int sprintf(char *str, const char *format, ...);
参数说明
-
str
:目标字符数组(缓冲区),用于存储格式化后的字符串。 -
format
:格式化字符串(同printf
),包含普通字符和格式说明符(如%d
、%s
等)。 -
...
:可变参数列表,需与format
中的格式说明符匹配的数据。
返回值
-
成功:返回写入到字符串中的字符总数 (不包含结尾的
\0
)。 -
失败:返回负数(通常为
-1
,表示格式化错误或缓冲区溢出)。
特点与用法
-
字符串拼接与格式化 :将多个数据按指定格式组合成一个字符串,比
strcat
更灵活。 -
数值转字符串:可将整数、浮点数等转换为字符串形式。
示例 1:拼接字符串与数值
cpp
#include <stdio.h>
int main() {
char buffer[100]; // 目标缓冲区
int id = 1001;
char name[] = "LiHua";
float score = 89.5f;
// 将数据格式化写入 buffer
int len = sprintf(buffer, "ID: %d, Name: %s, Score: %.1f", id, name, score);
printf("格式化结果:%s\n", buffer); // 输出:ID: 1001, Name: LiHua, Score: 89.5
printf("字符总数:%d\n", len); // 输出:32(不包含结尾的 \0)
return 0;
}
示例 2:数值转字符串
cpp
char num_str[20];
int num = 12345;
sprintf(num_str, "%d", num); // 将整数转为字符串 "12345"
1.2 sscanf
函数:从字符串中格式化读取数据
函数原型
cpp
#include <stdio.h>
int sscanf(const char *str, const char *format, ...);
参数说明
-
str
:源字符串,从中读取并解析数据。 -
format
:格式化字符串(同scanf
),指定读取数据的格式。 -
...
:可变参数列表,指向存储读取结果的变量指针(需与格式说明符匹配)。
返回值
-
成功:返回成功匹配并赋值的数据项数量。
-
失败:返回
EOF
(-1
),表示未匹配到任何数据。
特点与用法
-
字符串解析:从复杂字符串中提取指定格式的数据(如从日志字符串中提取时间、数值等)。
-
字符串转数值:将字符串形式的数字转换为整数、浮点数等类型。
示例 1:解析字符串中的数据
cpp
#include <stdio.h>
int main() {
char log[] = "2023-10-01 15:30, Temperature: 25.5, Humidity: 60";
int year, month, day, hour, minute, humidity;
float temp;
// 从 log 中按格式提取数据
int count = sscanf(log, "%d-%d-%d %d:%d, Temperature: %f, Humidity: %d",
&year, &month, &day, &hour, &minute, &temp, &humidity);
if (count == 7) { // 成功提取7项数据
printf("日期:%d-%d-%d %d:%d\n", year, month, day, hour, minute);
printf("温度:%.1f℃,湿度:%d%%\n", temp, humidity);
}
return 0;
}
输出:
cpp
日期:2023-10-01 15:30
温度:25.5℃,湿度:60%
示例 2:字符串转数值
cpp
char str[] = "123.45";
int int_val;
float float_val;
sscanf(str, "%d", &int_val); // 提取整数部分:int_val = 123
sscanf(str, "%f", &float_val); // 提取浮点数:float_val = 123.45
三、注意事项
-
缓冲区溢出风险 :
sprintf
不会检查目标缓冲区str
的大小,若写入内容超过缓冲区长度,会导致内存越界(覆盖其他数据)。建议使用更安全的snprintf
(限制最大写入长度):cpp// 最多写入 99 个字符(留 1 个给 \0) snprintf(buffer, 100, "ID: %d, Name: %s", id, name);
-
格式匹配要求 :
sscanf
依赖格式字符串与源字符串的严格匹配,若格式不符(如多余字符、缺少分隔符),会导致提取失败。例如:cppchar str[] = "age=20"; int age; sscanf(str, "age=%d", &age); // 正确匹配,age=20 sscanf(str, "%d", &age); // 匹配失败,返回 0
二. 文件的随机读写
2.1 fseek
函数:移动文件读写指针
fseek
用于将文件流的读写指针移动到指定位置,是文件随机访问的核心函数。
函数原型
cpp
#include <stdio.h>
int fseek(FILE *stream, long int offset, int origin);
参数说明
-
stream
:目标文件流(文件指针)。 -
offset
:偏移量(字节数),可正可负(正数表示向后移动,负数表示向前移动)。 -
origin
:起始位置(基准点),必须是以下宏之一:-
SEEK_SET
(0):从文件开头开始计算偏移。 -
SEEK_CUR
(1):从当前指针位置开始计算偏移。 -
SEEK_END
(2):从文件末尾开始计算偏移。
-
返回值
-
成功:返回
0
。 -
失败:返回非零值(如偏移量超出文件范围)。
用法示例
cpp
#include <stdio.h>
int main() {
FILE *fp = fopen("test.bin", "rb+"); // 二进制读写模式
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
// 1. 移动到文件开头后第10字节处(从开头偏移10)
fseek(fp, 10, SEEK_SET);
// 2. 从当前位置向后移动5字节
fseek(fp, 5, SEEK_CUR);
// 3. 移动到文件末尾前3字节处(从末尾偏移-3)
fseek(fp, -3, SEEK_END);
fclose(fp);
return 0;
}
注意事项
-
二进制文件 vs 文本文件:
-
二进制文件中,
fseek
可准确定位到任意字节位置。 -
文本文件中,由于换行符转换(如 Windows 下
\n
存储为\r\n
),SEEK_END
和负偏移可能导致定位不准,建议仅在二进制文件中使用随机访问。
-
-
读写模式要求 :文件需以允许读写的模式打开(如
r+
/rb+
等),否则定位可能失败。
2.2 ftell
函数:获取当前指针位置
ftell
用于获取文件流当前读写指针相对于文件开头的偏移量(字节数)。
函数原型
cpp
#include <stdio.h>
long int ftell(FILE *stream);
参数说明
stream
:目标文件流(文件指针)。
返回值
-
成功:返回当前指针位置(从文件开头算起的字节数)。
-
失败:返回
-1L
(long
类型的-1
)。
用法示例
cpp
#include <stdio.h>
int main() {
FILE *fp = fopen("test.bin", "rb");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
// 读取2个字符后,获取当前位置
fgetc(fp);
fgetc(fp);
long pos = ftell(fp);
printf("当前指针位置:%ld 字节(从文件开头算起)\n", pos); // 输出:2
fclose(fp);
return 0;
}
典型用途
-
计算文件大小(结合
fseek
定位到文件末尾):cppfseek(fp, 0, SEEK_END); // 移动到文件末尾 long file_size = ftell(fp); // 获取文件总字节数
-
记录当前位置,后续可通过
fseek
回到该位置。
2.3 rewind
函数:将指针重置到文件开头
rewind
是简化版的 fseek
,功能是将文件读写指针移动到文件开头,同时清除文件流的错误标志。
函数原型
cpp
#include <stdio.h>
void rewind(FILE *stream);
参数说明
stream
:目标文件流(文件指针)。
功能等价于
cpp
fseek(stream, 0L, SEEK_SET); // 移动到文件开头
clearerr(stream); // 清除错误标志
用法示例
cpp
#include <stdio.h>
int main() {
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
perror("文件打开失败");
return 1;
}
// 读取文件内容
char buf[100];
fgets(buf, 100, fp);
printf("第一次读取:%s", buf);
// 重置指针到开头,重新读取
rewind(fp);
fgets(buf, 100, fp);
printf("第二次读取:%s", buf); // 再次读取第一行
fclose(fp);
return 0;
}
总结
-
fseek
:灵活移动指针,支持从开头、当前位置、末尾计算偏移,是随机访问的核心。 -
ftell
:获取当前指针位置,常用于计算文件大小或记录位置。 -
rewind
:快速将指针重置到文件开头,简化常见定位操作。
这三个函数配合使用,可实现文件的随机读写(如修改中间数据、跳读特定块),但需注意文本文件的定位限制,优先在二进制文件中使用。
三. 文件缓冲区
在 C 语言文件操作中,文件缓冲区(File Buffer) 是内存中的一块块临时存储区域,用于缓和程序与外部存储设备(如硬盘)之间的读写速度差异,是提高文件操作效率的关键机制。
3.1 缓冲区的作用
- 平衡速度差异:CPU 和内存的读写速度远快于磁盘,缓冲区可批量处理数据(先攒一批数据再一次性写入磁盘,或一次性从磁盘读取一批数据到内存),减少磁盘 I/O 次数。
- 减少系统调用:每次直接读写磁盘需要操作系统介入(系统调用),开销较大;通过缓冲区批量操作,可大幅减少系统调用次数。
3.2 缓冲区的分类(按刷新策略)
C 语言标准库根据文件流类型,默认使用三种缓冲区策略:
缓冲区类型 | 适用场景 | 刷新时机(数据写入磁盘) |
---|---|---|
全缓冲 | 普通文件(如 test.txt ) |
1. 缓冲区满时 2. 调用 fclose 关闭文件时 3. 手动调用 fflush 时 |
行缓冲 | 标准输出流 stdout |
1. 遇到换行符 \n 时 2. 缓冲区满时 3. 关闭文件或手动刷新时 |
无缓冲 | 标准错误流 stderr |
数据直接写入设备(无缓冲),确保错误信息及时显示 |
3.3 缓冲区的操作函数
fflush
:手动刷新缓冲区
强制将缓冲区中的数据写入磁盘(或输出设备),避免数据滞留内存。
cpp
#include <stdio.h>
int fflush(FILE *stream);
- 参数:
stream
为目标文件流(NULL
表示刷新所有输出流)。 - 返回值:成功返回
0
;失败返回EOF
。
示例:
cpp
FILE *fp = fopen("data.txt", "w");
fputs("Hello", fp); // 数据暂存在缓冲区,未写入磁盘
fflush(fp); // 手动刷新,数据写入磁盘
setvbuf
:自定义缓冲区
在打开文件后、进行读写操作前,可通过 setvbuf
手动设置缓冲区的类型和大小。
cpp
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size);
- 参数:
stream
:目标文件流。buf
:自定义缓冲区地址(NULL
表示使用系统自动分配的缓冲区)。mode
:缓冲模式(_IOFBF
全缓冲、_IOLBF
行缓冲、_IONBF
无缓冲)。size
:缓冲区大小(字节数)。
- 返回值:成功返回
0
;失败返回非零。
示例:
cpp
FILE *fp = fopen("test.txt", "w");
char mybuf[1024];
// 设置为全缓冲,使用自定义缓冲区(大小1024字节)
setvbuf(fp, mybuf, _IOFBF, 1024);