Read file line by line using ifstream in C++
file.txt的内容包括:
1 2 3 4 5 6 7 | 5 3 6 4 7 1 10 5 11 6 12 3 12 4 |
其中,
我可以得到第一行,但如何得到文件的下一行?
1 2 | ifstream myfile; myfile.open ("text.txt"); |
首先,制作一个
1 2 | #include <fstream> std::ifstream infile("thefile.txt"); |
两种标准方法是:
假设每行由两个数字组成,并逐个标记地读取:
1 2 3 4 5 | int a, b; while (infile >> a >> b) { // process pair (a,b) } |
基于行的分析,使用字符串流:
1 2 3 4 5 6 7 8 9 10 11 12 | #include <sstream> #include <string> std::string line; while (std::getline(infile, line)) { std::istringstream iss(line); int a, b; if (!(iss >> a >> b)) { break; } // error // process pair (a,b) } |
您不应该混合(1)和(2),因为基于令牌的解析不会吞掉新行,因此,如果在基于令牌的提取使您到达行的末尾之后使用
使用
1 | std::ifstream input("filename.ext" ); |
如果您确实需要逐行阅读,请执行以下操作:
1 2 3 4 | for( std::string line; getline( input, line ); ) { ...for each line in input... } |
但您可能只需要提取坐标对:
1 2 | int x, y; input >> x >> y; |
更新:
在代码中使用
在C++中逐行读取文件可以以不同的方式完成。
[fast]使用std::getline()循环最简单的方法是使用std::getline()调用打开std::ifstream并循环。代码清晰易懂。
1 2 3 4 5 6 7 8 9 10 11 | #include <fstream> std::ifstream file(FILENAME); if (file.is_open()) { std::string line; while (getline(file, line)) { // using printf() in all tests for consistency printf("%s", line.c_str()); } file.close(); } |
[快速]使用Boost的文件描述源
另一种可能是使用Boost库,但是代码会变得更加冗长。性能与上面的代码非常相似(使用std::getline()循环)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <boost/iostreams/device/file_descriptor.hpp> #include <boost/iostreams/stream.hpp> #include <fcntl.h> namespace io = boost::iostreams; void readLineByLineBoost() { int fdr = open(FILENAME, O_RDONLY); if (fdr >= 0) { io::file_descriptor_source fdDevice(fdr, io::file_descriptor_flags::close_handle); io::stream <io::file_descriptor_source> in(fdDevice); if (fdDevice.is_open()) { std::string line; while (std::getline(in, line)) { // using printf() in all tests for consistency printf("%s", line.c_str()); } fdDevice.close(); } } } |
[最快]使用C代码
如果性能对软件至关重要,您可以考虑使用C语言。此代码可以比上面的C++版本快4-5倍,参见下面的基准
1 2 3 4 5 6 7 8 9 10 11 12 13 | FILE* fp = fopen(FILENAME,"r"); if (fp == NULL) exit(EXIT_FAILURE); char* line = NULL; size_t len = 0; while ((getline(&line, &len, fp)) != -1) { // using printf() in all tests for consistency printf("%s", line); } fclose(fp); if (line) free(line); |
基准——哪个更快?
我用上面的代码做了一些性能基准测试,结果很有趣。我用包含100000行、1000000行和10000000行文本的ASCII文件测试了代码。每行文本平均包含10个单词。该程序是用
结果显示每段代码读取文件所用的时间(毫秒)。
两种C++方法之间的性能差异很小,在实践中不应该有任何差别。C代码的性能使基准测试令人印象深刻,并且在速度方面可以改变游戏规则。
1 2 3 4 | 10K lines 100K lines 1000K lines Loop with std::getline() 105ms 894ms 9773ms Boost code 106ms 968ms 9561ms C code 23ms 243ms 2397ms |
既然坐标是成对的,为什么不为它们编写一个结构呢?
1 2 3 4 5 | struct CoordinatePair { int x; int y; }; |
然后可以为IStream编写重载的提取运算符:
1 2 3 4 5 6 | std::istream& operator>>(std::istream& is, CoordinatePair& coordinates) { is >> coordinates.x >> coordinates.y; return is; } |
然后你可以把一个坐标文件直接读取到一个向量中,就像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | #include <fstream> #include <iterator> #include <vector> int main() { char filename[] ="coordinates.txt"; std::vector<CoordinatePair> v; std::ifstream ifs(filename); if (ifs) { std::copy(std::istream_iterator<CoordinatePair>(ifs), std::istream_iterator<CoordinatePair>(), std::back_inserter(v)); } else { std::cerr <<"Couldn't open" << filename <<" for reading "; } // Now you can work with the contents of v } |
如果输入为:
1 2 3 | 1,NYC 2,ABQ ... |
您仍然可以应用相同的逻辑,如:
1 2 3 4 5 6 7 8 9 10 11 12 | #include <fstream> std::ifstream infile("thefile.txt"); if (infile.is_open()) { int number; std::string str; char c; while (infile >> number >> c >> str && c == ',') std::cout << number <<"" << str <<" "; } infile.close(); |
这是将数据加载到C++程序中的通用解决方案,并使用RealLoad函数。这可以为csv文件修改,但分隔符是一个空格。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | int n = 5, p = 2; int X[n][p]; ifstream myfile; myfile.open("data.txt"); string line; string temp =""; int a = 0; // row index while (getline(myfile, line)) { //while there is a line int b = 0; // column index for (int i = 0; i < line.size(); i++) { // for each character in rowstring if (!isblank(line[i])) { // if it is not blank, do this string d(1, line[i]); // convert character to string temp.append(d); // append the two strings } else { X[a][b] = stod(temp); // convert string to double temp =""; // reset the capture b++; // increment b cause we have a new number } } X[a][b] = stod(temp); temp =""; a++; // onto next row } |
此答案适用于Visual Studio 2017,如果您想从文本文件中读取与编译的控制台应用程序相关的位置。
首先将文本文件(本例中为test.txt)放入解决方案文件夹。编译后,将文本文件与applicationname.exe保存在同一文件夹中
C:users"username"source
epos"solutionname""solutionname"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <iostream> #include <fstream> using namespace std; int main() { ifstream inFile; // open the file stream inFile.open(".\\test.txt"); // check if opening a file failed if (inFile.fail()) { cerr <<"Error opeing a file" << endl; inFile.close(); exit(1); } string line; while (getline(inFile, line)) { cout << line << endl; } // close the file stream inFile.close(); } |
虽然不需要手动关闭文件,但如果文件变量的范围更大,最好这样做:
1 2 3 4 5 6 7 8 9 | ifstream infile(szFilePath); for (string line =""; getline(infile, line); ) { //do something with the line } if(infile.is_open()) infile.close(); |