C/C++程序写入文本文件时出现垃圾行的问题排查
问题描述
我有一个程序每2分钟输出一行制表符分隔的数字文本(每行最多51字符),为保留最后100行,先编写了C++版FIFO缓存程序,将其标准输出导入该程序。初始运行正常,但一段时间后文件末尾出现无效垃圾行。随后将该功能整合到原C程序中,采用相同算法,问题依旧。调试发现used变量始终未超过100,且原程序单独输出无异常。运行环境为Angstrom Linux v2018.12(DE10-Standard板),编译未开优化也无改善,请问问题原因是什么?
C++版本代码
#include <iostream> #include <string> #include <fstream> using namespace std; string lines [100]; int used=0; ofstream outfile("last100.txt", ios_base::out); int main() { for (string line; getline(cin, line);) { if(used<100) //adds new lines to the end of the array while not full { lines[used]=line; used++; } else //if full, then shift lines one position back and add on the end { for(int i=0;i<99;i++) { lines[i]=lines[i+1]; } lines[99]=line; } outfile.seekp(0, ios::beg); //seek to the beginning of file for(int i=0;i<used;i++) //save the array to the text file and flush { outfile.write((lines[i]+'\n').data(),(lines[i]+'\n').size()); outfile.flush(); } } return 0; }
C版本关键代码
char lines[100][256]; int used = 0; int main(int argc, char *argv[]) { FILE *outfile = fopen("last100.txt", "w"); if (outfile == NULL) { printf("Error opening file for writing.\n"); return 1; } // other pieces of the program come here if (used < 100) //again, add to the end if not full { snprintf (lines[used],256, /* a bunch of integer variables come here */); used++; } else //if full, shift lines one position back and add to the last position { for (int i = 0; i < 99; i++) { strcpy(lines[i], lines[i+1]); } snprintf (lines[99],256, /* a bunch of integer variables come here */); } rewind(outfile); //seek to the beginning of the file for (int i = 0; i < used; i++) //write to the file and flush { fprintf(outfile, "%s\n", lines[i]); fflush(outfile); } }
问题原因及解决方案
核心原因
问题出在文件写入后未截断多余内容:每次将文件指针移到开头重新写入时,如果本次写入的总字节数小于文件之前的长度,文件末尾未被覆盖的旧数据会保留下来,形成所谓的"垃圾行"。
初始运行正常是因为程序刚启动时,used从0逐渐增加到100,每次写入的总长度都比之前更长,旧内容会被完全覆盖;当used稳定在100后,如果某次写入的总字节数(比如某行内容比之前的短)小于之前文件的长度,就会留下未被覆盖的旧数据,表现为末尾的垃圾行。
修复方案
C++版本修复
在写完所有行后,截断文件到当前写入的位置:
// 在写完所有行的循环之后添加 outfile.seekp(0, ios::end); outfile.truncate();
优化后的写入部分代码:
outfile.seekp(0, ios::beg); //seek to the beginning of file for(int i=0;i<used;i++) //save the array to the text file { outfile.write((lines[i]+'\n').data(),(lines[i]+'\n').size()); } outfile.seekp(0, ios::end); outfile.truncate(); outfile.flush();
(注:无需在循环内每次flush,写完所有行后统一flush即可提升效率)
C版本修复
利用ftruncate函数截断文件,需包含<unistd.h>头文件:
// 在写完所有行的循环之后添加 ftruncate(fileno(outfile), ftell(outfile));
优化后的写入部分代码:
rewind(outfile); //seek to the beginning of the file for (int i = 0; i < used; i++) //write to the file { fprintf(outfile, "%s\n", lines[i]); } ftruncate(fileno(outfile), ftell(outfile)); fflush(outfile);
同样,循环内无需每次flush,统一flush即可。
内容的提问来源于stack exchange,提问作者user2934303
相关产品推荐
相关产品推荐

