C++如何修改文件中的特定行

时间:2022-05-01 10:00:16

最近要处理一些数据集文件,转换为图像的格式,但是这种文件的格式并不是非常的确定,因此在处理的过程中遇到了种种bug,其中有一个bug的原因就是文件末尾的回车少了,我需要把这种文件的这些行分别加上回车。于是就有了这篇博客。下面来详细记录一下问题的解决方法。

一、问题描述

我需要处理的文件片段如下:
<ink xmlns="http://www.w3.org/2003/InkML">
<traceFormat>
<channel name="X" type="decimal"/>
<channel name="Y" type="decimal"/>
</traceFormat>
<annotation type="age">25</annotation>
<annotation type="gender">Male</annotation>
<annotation type="hand">right-handed</annotation>
<annotation type="writer">Uy1yeS7cQJKmcH/ktausFKn1BFo=</annotation>
<annotation type="truth">\sqrt{b^{2} - 4 a c}</annotation>
<annotation type="UI">2011_KAIST_Data2_14_sub_9</annotation>
<annotation type="copyright">KAIST</annotation>
<annotationXML type="truth" encoding="Content-MathML">
<math xmlns='http://www.w3.org/1998/Math/MathML'>
<msqrt xml:id="_1">
<msup>
<mi xml:id="b_1">b</mi>
<mn xml:id="2_1">2</mn>
</msup>
<mrow>
<mo xml:id="-_1">-</mo>
<mrow>
<mn xml:id="4_1">4</mn>
<mrow>
<mi xml:id="a_1">a</mi>
<mi xml:id="c_1">c</mi>
</mrow>
</mrow>
</mrow>
</msqrt>
</math>
</annotationXML>
<trace id="0">4935 3421, 4935 3421, 4994 3459, 5037 3486, 5084 3525, 5134 3568, 5188 3617, 5238 3667, 5283 3714, 5324 3757, 5355 3793, 5380 3818, 5400 3841, 5414 3850, 5414 3850, 5428 3834, 5428 3814, 5419 3778, 5407 3732, 5396 3674, 5382 3599, 5364 3513, 5344 3419, 5319 3321, 5294 3218, 5270 3116, 5249 3012, 5224 2911, 5206 2820, 5191 2739, 5179 2669, 5175 2603, 5175 2549, 5175 2506, 5175 2477, 5177 2454, 5179 2441, 5184 2432, 5184 2432, 5200 2418, 5213 2411, 5231 2407, 5249 2402, 5279 2400, 5312 2396, 5355 2391, 5407 2391, 5470 2387, 5547 2387, 5633 2387, 5737 2387, 5850 2384, 5978 2384, 6123 2384, 6274 2380, 6437 2375, 6608 2366, 6787 2360, 6970 2353, 7161 2344, 7356 2337, 7552 2330, 7751 2323, 7954 2317, 8153 2317, 8345 2305, 8530 2296, 8701 2287, 8859 2274, 9002 2260, 9130 2244, 9239 2226, 9336 2204, 9421 2183, 9485 2172, 9534 2156</trace>
<trace id="1">6035 2960, 6035 2960, 6035 2990, 6033 3014, 6028 3053, 6026 3105, 6021 3175, 6015 3254, 6008 3340, 6001 3428, 5997 3511, 5992 3583, 5992 3644, 5987 3692, 5987 3719, 5987 3732, 5987 3732, 5992 3710, 5999 3687, 6006 3658, 6015 3624, 6030 3588, 6051 3550, 6076 3513, 6103 3482, 6130 3457, 6157 3441, 6184 3432, 6206 3432, 6225 3446, 6234 3468, 6234 3502, 6227 3543, 6211 3592, 6191 3642, 6164 3696, 6136 3741, 6107 3782, 6078 3811, 6048 3829, 6021 3834, 6001 3827, 5983 3811, 5969 3791, 5969 3759</trace>
<trace id="2">6439 2922, 6439 2922, 6446 2888, 6455 2877, 6466 2868, 6480 2868, 6493 2872, 6502 2886, 6516 2904, 6523 2926, 6529 2953, 6536 2985, 6547 3010, 6563 3035, 6586 3050, 6615 3053, 6656 3046, 6694 3035</trace>
就是把含有<trace id="i">的这些行中,让数据单独占一行,让<trace id="i">和</trace>单独占一行。

二、解决方案

感觉上面的需求很简单,实际上实现起来还挺麻烦的,首先我想找到这一行,然后找到<trace id="i">然后在他后面加回车符,然后写会文件,但是这样做会写在文件的末尾,C语言中的fseek函数虽然可以定位文件中的指针,让他在特定位置进行读写操作,但是又比较麻烦(实际上是我C语言几乎忘光了)。
出于懒得原因,我想到了一种简单的方法,就是首先我按行读取原文件,判断该行是不是我要操作的,如果不是,则把他逐行写入一个新的临时文件中去,如果是我要操作的行,那么我就先把<trace id="i">写入新的文件,然后写入换行符,再写入数据,在写入换行符,再写入</trace>。最后写完临时文件以后,再读取临时文件,重新写入到原文件中,最终删除临时文件。
虽然这样表述着比较麻烦,但是这是一种懒人的做法,效率也不会很高,但是实现起来很便捷。
代码如下(Windows下实现):
void readAndWrite(const string& filepath)
{
fstream file(filepath);
string line;
int n, count = 0;
//create a temp file
ofstream outfile("1\\tmp.inkml", ios::out | ios::trunc);
//read the original file
while (!file.eof())
{
getline(file, line);//read one line
//judge the line just read is the line to process
if (strstr(line.c_str(), "<trace") && (strstr(line.c_str(), "id =") ||
strstr(line.c_str(), "id=")) && !strstr(line.c_str(), "traceGroup"))
{
//find the end of <trace id="i">
const char* t = strstr(line.c_str(), ">") + 1;
int i = 0;
//write <trace id="i"> to the temp file
while (line[i] != '>' && i<line.length())
{
outfile << line[i];
i++;
}
outfile << '>';
//write \n
outfile << endl;
int j = 0;
//write the data
while (*(t + j) != '<' && j<strlen(t))
{
outfile << *(t + j);
j++;
}
//write \n
outfile << endl;
//write </trace>
outfile << t + j << endl;
}
else
outfile << line << endl;;
}
outfile.close();
file.close();
ofstream outfile1(filepath, ios::out | ios::trunc);
fstream file1("1\\tmp.inkml");
//write the temp file to the original file
while (!file1.eof())
{
getline(file1, line);
outfile1 << line << endl;
}
outfile1.close();
file1.close();
//delete the temp file
system("del 1\\tmp.inkml");
}