【问题标题】:Most efficient way of creating a progress bar while reading input from a file从文件读取输入时创建进度条的最有效方法
【发布时间】:2014-05-01 01:48:59
【问题描述】:

我有一个程序从一个相对较大的文件中读取输入,数千行。

话虽如此,我想在处理文件时实现一个进度条指示器。但是,我知道的大多数方法都要求您使用getLine 来计算文件中有多少行,以将其用作进度条(Boost Example) 的“预定义目标”。这意味着我必须遍历一个大文本文件两次,一次计算行数,另一次实际获取每一行并显示进度条。

有没有更有效的方法?

【问题讨论】:

  • 这就是为什么大多数进度条实际上并不代表进度。它们只是向用户显示正在取得进展的猜测。
  • 您可以在几秒钟内阅读一百万行。除非您的处理非常密集,否则您可能根本不需要这个东西。或者你可以只用字节而不是行来计算进度。
  • 按字节数,而不是行数。除非您从管道中读取,否则您可以得到它。
  • 另一种可能性是寻找文件的末尾,只是为了了解输入的大小。然后,将大小除以 10 或 100,并在检查当前读取文件的位置时不断增加进度条。这应该会给你一个非常漂亮和简单的进度条——可以用 ASCII-art 和回车(\r)来美化它。
  • @Rubens:为什么不发布您有效且有价值的答案?

标签: c++


【解决方案1】:

一个可能的解决方案是寻找文件的末尾,只是为了了解输入的大小。然后,根据您已处理的文件的百分比不断更新进度条。这应该会给你一个非常漂亮和简单的进度条——它可以用 ASCII-art 和回车符 (\r) 美化。

这也是一种可能的实现方式:

# include <cmath>
# include <string>
# include <fstream>
# include <iomanip>
# include <iostream>


class reader : public std::ifstream {

public:

    // Constructor
    template <class... Args>
    inline reader(int max, Args&&... args) :
    std::ifstream(args...), _max(max), _last(0) {
        if (std::ifstream::is_open()) _measure();
    }

    // Opens the file and measures its length
    template <class... Args>
    inline auto open(Args&&... args)
    -> decltype(std::ifstream::open(args...)) {
        auto rvalue(std::ifstream::open(args...));
        if (std::ifstream::is_open()) _measure();
        return rvalue;
    }

    // Displays the progress bar (pos == -1 -> end of file)
    inline void drawbar(void) {

        int pos(std::ifstream::tellg());
        float prog(pos / float(_length)); // percentage of infile already read
        if (pos == -1) { _print(_max + 1, 1); return; }

        // Number of #'s as function of current progress "prog"
        int cur(std::ceil(prog * _max));
        if (_last != cur) _last = cur, _print(cur, prog);

    }

private:

    std::string _inpath;
    int _max, _length, _last;

    // Measures the length of the input file
    inline void _measure(void) {
        std::ifstream::seekg(0, end);
        _length = std::ifstream::tellg();
        std::ifstream::seekg(0, beg);
    }

    // Prints out the progress bar
    inline void _print(int cur, float prog) {

        std::cout << std::fixed << std::setprecision(2)
            << "\r   [" << std::string(cur, '#')
            << std::string(_max + 1 - cur, ' ') << "] " << 100 * prog << "%";

        if (prog == 1) std::cout << std::endl;
        else std::cout.flush();

    }

};


int main(int argc, char *argv[]) {

    // Creating reader with display of length 100 (100 #'s)
    reader infile(std::atoi(argv[2]), argv[1]);
    std::cout << "-- reading file \"" << argv[1] << "\"" << std::endl;

    std::string line;
    while (std::getline(infile, line)) infile.drawbar();

}

输出是这样的:

$ ./reader foo.txt 50              # ./reader <inpath> <num_#'s>
-- reading file "foo.txt"
   [###################################################] 100.00%

请注意,参数是输入文件和进度条中所需的# 数。我已将 length-seek 添加到 std::ifstream::open 函数中,但 drawbar() 是由用户调用的。你可以在std::ifstream 的特定函数中插入这个函数。

如果你想让它更漂亮,你也可以使用命令tput cols 来了解当前 shell 中的列数。此外,您可以将这样的命令放在可执行文件中,以使其更清晰

$ ./reader foo.txt $(( $(tput cols) - 30 ))
-- reading file "foo.txt"
   [####################################################################] 100.00%

正如其他人指出的那样,此解决方案不适用于管道和临时文件,在这种情况下,您手头没有输入长度。非常感谢@NirMH,非常友好的评论。

【讨论】:

  • 您能否提供一个非 O.O.P 解决方案。谢谢
猜你喜欢
  • 1970-01-01
  • 2013-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多