C++在终端、文件中就地覆盖输出的方法
1、在终端就地覆盖输出字符
先上效果图,如图1-1所示:
对应的程序如下:
#include <iostream>
#include <unistd.h>
int main(int argc, char* argv[])
{
// 设置为无缓冲
setbuf(stdout, NULL);
// 隐藏光标
std::cout << "\033[?25l";
for (int i = 1; i <= 10; i++)
{
std::cout << i << "\r";
sleep(1);
}
// 输出换行并恢复光标(光标不见了可以在终端执行 echo -e "\033[?25h" 就可以了)
std::cout << std::endl << "\033[?25h";
}
这个程序存在着一定的问题,如果是倒计时的话效果如图1-2所示:
可以看到一开始输出了10,占两个字符位置,后面由于都是输出单字符的数字,所以10后面的那个0一直停留在那里独自抱窝。对程序进行完善修改如下:
#include <iostream>
#include <unistd.h>
int count_bit(int number)
{
int count = 1;
while (number /= 10)
{
count++;
}
return count;
}
int main(int argc, char* argv[])
{
// 设置为无缓冲
setbuf(stdout, NULL);
// 隐藏光标
std::cout << "\033[?25l";
int last_bit = 0;
#if 1
for (int i = 1000000; i > 0; i /= 10)
#else
for (int i = 1; i <= 10; i++)
#endif
{
int cur_bit = count_bit(i);
std::cout << i;
int dif = last_bit - cur_bit;
if (dif > 0)
std::cout << std::string(dif, ' ');
std::cout << "\r";
last_bit = cur_bit;
sleep(1);
}
// 输出换行并恢复光标(光标不见了可以在终端执行 echo -e "\033[?25h" 就可以了)
std::cout << std::endl << "\033[?25h";
}
这次来个跨度比较大的迭代输出,这样可以更加直观看到我们的程序已经修改正确,如图1-3所示:
2、在文件中就地覆盖输出字符
前面做了一个在终端就地覆盖输出字符的程序,现在来探讨下在文件中就地覆盖字符该怎么做。首先,试试第一节那个方法可不可以直接使用,程序如下:
#include <iostream>
#include <fstream>
#include <unistd.h>
int count_bit(int number)
{
int count = 1;
while (number /= 10)
{
count++;
}
return count;
}
int main(int argc, char* argv[])
{
std::ofstream ofs("test", std::ios::out);
if (!ofs)
{
std::cout << "打开文件出错" << std::endl;
return -1;
}
int last_bit = 0;
#if 1
for (int i = 1000000; i > 0; i /= 10)
#else
for (int i = 1; i <= 10; i++)
#endif
{
int cur_bit = count_bit(i);
ofs << i;
int dif = last_bit - cur_bit;
if (dif > 0)
ofs << std::string(dif, ' ');
ofs << "\r";
last_bit = cur_bit;
sleep(1);
}
}
输出文件内容如下:
1000000
100000
10000
1000
100
10
1
看来不能直接使用了,得另辟蹊径了。设计思路如下:
- 我们以读写的方式打开文件,如果文件不存在则创建(这里要注意不要用追加方式打开,不然就不能自由通过设置偏移量来达到对局部进行写入操作了,因为以追加打来文件之后写操作的偏移就失效了)
- 打开文件后,先把原先的内容记录下来(假设我们文件只有一行内容,记录着一个数字),然后对这个值进行各种操作(增加、减少都行)
- 操作完成之后,通过设置偏移量到文件开头,把数字写回去文件中,达到覆盖前面内容的目的
测试程序如下:
#include <iostream>
#include <fstream>
#include <unistd.h>
int count_bit(long number)
{
int count = 1;
while (number /= 10)
{
count++;
}
return count;
}
int main(int argc, char* argv[])
{
std::fstream f;
f.open("test", std::ios::out | std::ios::in);
if (!f.is_open())
{
f.open("test", std::ios::out | std::ios::in | std::ios::trunc);
if (!f.is_open())
{
std::cout << "打开文件出错" << std::endl;
return -1;
}
}
std::string line;
long number;
if (std::getline(f, line) == 0)
{
number = 1000000;
std::cout << "第一次打开文件,设置初始值为1000000" << std::endl;
}
else
{
number = atol(line.c_str());
std::cout << "打开文件成功,读取到的初始值为" << number << std::endl;
}
int last_bit = count_bit(number);
// 这里自由发挥,加减乘除随便弄
number /= 10;
if (number == 0)
number = 1000000;
int cur_bit = count_bit(number);
int dif = last_bit - cur_bit;
// 清除流状态,必须加
f.clear();
f.seekp(0, std::ios::beg);
f << number;
if (dif > 0)
f << std::string(dif, ' ');
}
这里就不贴结果图了,大家可以自行去尝试一下,就是简单地通过偏移写指针来达到往指定区域写入的内容的目的,这样也能覆盖掉原先的内容了。
3、总结
本文讲了两个简单的例子,第一个例子可以用作终端对某些操作进行倒计时或计数;第二个例子可以用于每天简单地记录某些批处理任务执行的次数,比如爬虫爬了多少内容之类的,当然其它语言大家就得按着这个思路自己去改改了。要注意这个也只能做点简单的记录了,复杂的记录还是得做一个规范的日志文件。
最后,如果大家觉得本文写得好的话麻烦点赞收藏关注一下谢谢,也可以关注该专栏,以后会有更多优质文章输出的。