C++ 从文件读取输入时创建进度条的最有效方法
我有一个程序,它从一个相对较大的文件中读取输入,数千行长 说到这里,我想在处理文件时实现一个进度条指示器。但是,我知道的大多数方法都要求您使用C++ 从文件读取输入时创建进度条的最有效方法,c++,C++,我有一个程序,它从一个相对较大的文件中读取输入,数千行长 说到这里,我想在处理文件时实现一个进度条指示器。但是,我知道的大多数方法都要求您使用getLine来计算文件中有多少行,以便将其用作进度条的“预定义目标”。这意味着我必须在一个大的文本文件中循环两次,一次用来计算有多少行,另一次用来实际获取每一行并显示进度条 有没有更有效的方法?一个可能的解决方案是查找文件的末尾,只了解输入的大小。然后,根据已处理文件的百分比不断更新进度条。这将为您提供一个非常漂亮且简单的进度条——可以使用ASCII艺术
getLine
来计算文件中有多少行,以便将其用作进度条的“预定义目标”。这意味着我必须在一个大的文本文件中循环两次,一次用来计算有多少行,另一次用来实际获取每一行并显示进度条
有没有更有效的方法?一个可能的解决方案是查找文件的末尾,只了解输入的大小。然后,根据已处理文件的百分比不断更新进度条。这将为您提供一个非常漂亮且简单的进度条——可以使用ASCII艺术和回车(\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();
}
正如其他人所指出的,此解决方案不能正确处理管道和临时文件,在这种情况下,您手头没有输入长度。非常感谢@NirMH的好意评论。这就是为什么大多数进度条实际上并不代表进度。它们只是猜测,向用户显示正在取得进展。你可以在几秒钟内阅读一百万行。除非你的处理非常密集,否则你可能根本不需要这个东西。或者你可以用字节而不是行来计算进度。按字节计数,而不是行计数。除非你是从管道中读取,否则你可以得到它。另一种可能是寻找文件的结尾,只是为了了解输入的大小。然后,将大小除以10或100,并在检查读取文件的当前位置时不断增加进度条。这应该会给你一个非常漂亮和简单的进度条——可以用ASCII艺术和回车(
\r
)来修饰它。@Rubens:为什么不发布你的有效和有价值的答案呢?你能提供一个非O.O.P的解决方案吗。谢谢
$ ./reader foo.txt 50 # ./reader <inpath> <num_#'s>
-- reading file "foo.txt"
[###################################################] 100.00%
$ ./reader foo.txt $(( $(tput cols) - 30 ))
-- reading file "foo.txt"
[####################################################################] 100.00%