文件file.txt的内容为:
5 3
6 4
7 1
10 5
11 6
12 3
12 4
其中5 3
是一组坐标对。
如何在C++中逐行处理此数据?
我能够获取第一行,但如何获取文件的下一行?
ifstream myfile;
myfile.open ("file.txt");
首先,创建一个 ifstream
:
#include <fstream>
std::ifstream infile("thefile.txt");
两种标准方法是:
假设每一行都由两个数字组成,并逐个令牌进行读取:
int a, b;
while (infile >> a >> b)
{
// process pair (a,b)
}
使用字符串流进行基于行的解析:
#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),因为基于令牌的解析不会将换行符一起处理,所以如果你在基于令牌的提取结束之后使用getline()
,可能会得到错误的空行。
int a, b; char c; while ((infile >> a >> c >> b) && (c == ','))
- Kerrek SBwhile(getline(f, line)) { }
结构的解释和错误处理,请参考这篇(我的)文章:http://gehrcke.de/2011/06/reading-files-in-c-using-ifstream-dealing-correctly-with-badbit-failbit-eofbit-and-perror/ (我认为我没有必要因在这里发布而感到内疚,它甚至略早于这个答案)。 - Dr. Jan-Philip Gehrcke使用ifstream
从文件读取数据:
std::ifstream input( "filename.ext" );
如果你确实需要逐行读取,那么请这样做:
for( std::string line; getline( input, line ); )
{
...for each line in input...
}
但是你可能只需要提取坐标对:
int x, y;
input >> x >> y;
更新:
在你的代码中使用了ofstream myfile;
,然而ofstream
中的o
代表着output
(输出)。如果你想要从文件中读取数据(输入),请使用ifstream
。如果你想要读写两个操作同时进行,请使用fstream
。
在C++中逐行读取文件有几种不同的方法。
最简单的方法是打开一个std::ifstream并使用std::getline()循环调用。 代码干净易懂。
#include <fstream>
std::ifstream file(FILENAME);
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
// using printf() in all tests for consistency
printf("%s", line.c_str());
}
file.close();
}
另一种可能性是使用Boost库,但代码会变得更加冗长。性能与上述代码(使用std::getline()循环)相当。
#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++版本快4-5倍,见下面的基准测试。
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);
我对上面的代码进行了一些性能基准测试,结果很有趣。我使用包含100,000行、1,000,000行和10,000,000行文本的ASCII文件对代码进行了测试。每行文本平均包含10个单词。程序使用-O3
优化编译,并将输出转发到/dev/null
以从测量中删除日志时间变量。最后,每段代码都使用printf()
函数记录每行以保持一致性。
结果显示了每段代码读取文件所需的时间(以毫秒为单位)。
C++两种方法之间的性能差异微不足道,在实践中不应该有任何区别。C代码的性能是使基准测试印象深刻并且在速度方面可以改变游戏规则的因素。
10K lines 100K lines 1000K lines
Loop with std::getline() 105ms 894ms 9773ms
Boost code 106ms 968ms 9561ms
C code 23ms 243ms 2397ms
std::cout
相对于printf
的默认行为的已知劣势。 - user4581301printf()
函数以保持一致性。我也尝试在所有情况下使用std::cout
,但是没有任何区别。正如我在文本中所描述的那样,程序的输出被发送到/dev/null
,因此打印行的时间不会被计算。 - HugoTeixeiragetline
函数是 GNU 扩展(现已添加到 POSIX 标准),它不是标准的 C 函数。 - Dan M.既然你的坐标是成对出现的,为什么不为它们编写一个结构体呢?
struct CoordinatePair
{
int x;
int y;
};
然后您可以为istream编写重载的提取运算符:
std::istream& operator>>(std::istream& is, CoordinatePair& coordinates)
{
is >> coordinates.x >> coordinates.y;
return is;
}
然后你可以像这样直接将一个坐标文件读入到向量中:
#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\n";
}
// Now you can work with the contents of v
}
operator>>
中无法从流中读取两个 int
标记时会发生什么?当使用回溯分析器(即当 operator>>
失败时,将流回滚到以前的位置并返回 false 或类似内容)时,如何使其正常工作? - fferriint
标记,则 is
流将评估为 false
,并且读取循环将在该点终止。您可以通过检查各个读取的返回值来在 operator>>
中检测此情况。如果要回滚流,则应调用 is.clear()
。 - user325117operator>>
中,更正确的说法是 is >> std::ws >> coordinates.x >> std::ws >> coordinates.y >> std::ws;
,因为否则你假设输入流处于跳过空格模式。 - Darko Veberic扩展接受的答案,如果输入是:
1,NYC
2,ABQ
...
#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 << "\n";
}
infile.close();
虽然没有必要手动关闭文件,但如果文件变量的作用域更大,手动关闭文件是一个好主意:
ifstream infile(szFilePath);
for (string line = ""; getline(infile, line); )
{
//do something with the line
}
if(infile.is_open())
infile.close();
ifstream infile(szFilePath);
超出作用域时,文件不是已经关闭了吗? - KcFnMi#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();
}
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
}