一句话理解 fputs
「将字符串像‘信件’一样完整投递到文件或屏幕,但不会偷偷塞入‘信封封条’(换行符)!」
函数原型
#include
int fputs(const char *str, FILE *stream);
入口参数
参数 | 类型 | 比喻解释 |
str | const char* | 要投递的「信件内容」(字符串) |
stream | FILE* | 信件的「目的地」(文件/屏幕) |
返回参数
返回值 | 含义 |
非负数 | 投递成功(返回写入的字符数,不保证) |
EOF | 投递失败(如磁盘满/文件未打开) |
核心功能图解
const char *msg = "Hello World";
fputs(msg, stdout); → 屏幕显示:Hello World
fputs(msg, file); → 文件内容:Hello World
代码实例:信件投递实战
场景1:创建日志文件(自动追加时间戳)
#include
#include
void log_message(const char *text) {
FILE *log = fopen("app.log", "a"); // 追加模式打开日志
if (!log) {
perror(" 日志本打不开");
return;
}
// 获取时间戳
time_t now = time(NULL);
struct tm *t = localtime(&now);
char timestamp[20];
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", t);
// 写入日志(格式:时间 + 信息)
fputs("[", log);
fputs(timestamp, log);
fputs("] ", log);
fputs(text, log);
fputs("\n", log); // 手动换行
fclose(log);
}
int main() {
log_message("程序启动成功");
log_message("用户Alice登录系统");
return 0;
}
日志文件内容:
[2023-10-05 14:30:00] 程序启动成功
[2023-10-05 14:30:02] 用户Alice登录系统
场景2:实现终端进度条(无换行特性)
#include
#include // 用于sleep
int main() {
for (int i = 0; i <= 100; i += 10) {
printf("进度:"); // printf自带缓冲区
fputs("[", stdout); // 直接输出左括号
for (int j = 0; j < i/10; j++) {
fputs("█", stdout); // 用方块填充进度
}
fprintf(stdout, "] %d%%\r", i); // \r回到行首
fflush(stdout); // 强制刷新显示
sleep(1);
}
return 0;
}
动态效果:
进度:[██████████] 100% (动态增长)
场景3:文件内容合并工具
#include
void merge_files(const char *file1, const char *file2, const char *output) {
FILE *f1 = fopen(file1, "r");
FILE *f2 = fopen(file2, "r");
FILE *out = fopen(output, "w");
if (!f1 || !f2 || !out) {
perror("文件打开失败");
return;
}
char buffer[1024];
// 合并第一个文件
while (fgets(buffer, sizeof(buffer), f1) {
fputs(buffer, out);
}
// 合并第二个文件
while (fgets(buffer, sizeof(buffer), f2) {
fputs(buffer, out);
}
fclose(f1);
fclose(f2);
fclose(out);
}
int main() {
merge_files("part1.txt", "part2.txt", "complete.txt");
return 0;
}
技术细节剖析
1. 与 puts 的致命差异
特性 | fputs | puts |
换行符处理 | 不自动添加换行符 | 自动添加换行符 |
输出目标 | 任意文件流 | 只能输出到stdout |
性能 | 更高(无格式解析) | 稍低(隐含换行操作) |
2. 缓冲区安全规则
- 必须确保字符串以\0结尾:
char unsafe[3] = {'A', 'B', 'C'}; // 没有结束符!
fputs(unsafe, file); // 内存越界读取!
死亡陷阱:常见错误
1. 误用未初始化的指针
char *msg; // 未初始化!
fputs(msg, stdout); // 随机内存地址,程序崩溃!
2. 忽略返回值导致数据丢失
// 危险!不检查是否写入成功
fputs("重要数据", file);
// 安全写法:
if (fputs("重要数据", file) == EOF) {
perror("数据写入失败");
}
高级技巧:实现二进制协议封装
#include
void send_packet(FILE *network, const char *data, int length) {
// 协议头:4字节长度标识
char header[4];
header[0] = (length >> 24) & 0xFF;
header[1] = (length >> 16) & 0xFF;
header[2] = (length >> 8) & 0xFF;
header[3] = length & 0xFF;
fputs(header, network); // 写入头部
fputs(data, network); // 写入数据
fflush(network); // 强制发送
}
int main() {
// 模拟网络套接字
FILE *socket = fopen("network.bin", "wb");
send_packet(socket, "Hello Protocol!", 15);
fclose(socket);
return 0;
}
总结表格
特性 | 说明 |
核心优势 | 高效写入完整字符串 |
典型用途 | 日志记录、协议传输、数据拼接 |
安全准则 | 检查返回值,确保字符串合法 |
总结
- 像邮差投递信件:fputs 是C语言中最直接的字符串输出工具,专注于完整内容传递。
- 关键特性:
1 不添加额外换行符 → 精确控制输出格式
2 支持任意文件流 → 灵活选择输出目标
3 高性能无格式解析 → 效率碾压fprintf - 安全口诀:
「查地址(文件指针)、验内容(字符串)、盯回执(返回值)」