C++在终端文件中就地覆盖输出的方法

Posted 彼 方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++在终端文件中就地覆盖输出的方法相关的知识,希望对你有一定的参考价值。

1、在终端就地覆盖输出字符

先上效果图,如图1-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所示:

在这里插入图片描述

图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所示:

在这里插入图片描述

图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

看来不能直接使用了,得另辟蹊径了。设计思路如下:

  1. 我们以读写的方式打开文件,如果文件不存在则创建(这里要注意不要用追加方式打开,不然就不能自由通过设置偏移量来达到对局部进行写入操作了,因为以追加打来文件之后写操作的偏移就失效了)
  2. 打开文件后,先把原先的内容记录下来(假设我们文件只有一行内容,记录着一个数字),然后对这个值进行各种操作(增加、减少都行)
  3. 操作完成之后,通过设置偏移量到文件开头,把数字写回去文件中,达到覆盖前面内容的目的

测试程序如下:

#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、总结

本文讲了两个简单的例子,第一个例子可以用作终端对某些操作进行倒计时或计数;第二个例子可以用于每天简单地记录某些批处理任务执行的次数,比如爬虫爬了多少内容之类的,当然其它语言大家就得按着这个思路自己去改改了。要注意这个也只能做点简单的记录了,复杂的记录还是得做一个规范的日志文件。

最后,如果大家觉得本文写得好的话麻烦点赞收藏关注一下谢谢,也可以关注该专栏,以后会有更多优质文章输出的。

以上是关于C++在终端文件中就地覆盖输出的方法的主要内容,如果未能解决你的问题,请参考以下文章

C++在终端文件中就地覆盖输出的方法

我的Android进阶之旅NDK开发之在C++代码中使用Android Log打印日志,打印出C++的函数耗时以及代码片段耗时详情

用于输出的 Visual Studio 代码 C++ 终端

覆盖片段中的后退按钮

shell 常用

VSCode自定义代码片段——cli的终端命令大全