在 c + + 中使用 ifstream 逐行读取文件

The contents of file.txt are:

5 3
6 4
7 1
10 5
11 6
12 3
12 4

Where 5 3 is a coordinate pair. How do I process this data line by line in C++?

I am able to get the first line, but how do I get the next line of the file?

ifstream myfile;
myfile.open ("text.txt");

转载于:https://stackoverflow.com/questions/7868936/read-file-line-by-line-using-ifstream-in-c

7个回答

First, make an ifstream:

#include <fstream>
std::ifstream infile("thefile.txt");

The two standard methods are:

  1. Assume that every line consists of two numbers and read token by token:

    int a, b;
    while (infile >> a >> b)
    {
        // process pair (a,b)
    }
    
  2. Line-based parsing, using string streams:

    #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)
    }
    

You shouldn't mix (1) and (2), since the token-based parsing doesn't gobble up newlines, so you may end up with spurious empty lines if you use getline() after token-based extraction got you to the end of a line already.

weixin_41568174
from.. No, the boolean conversion checks for !fail(), not good(), which differs in the treatment of EOF (see here).
2 年多之前 回复
csdnceshi80
胖鸭 Thanks got the EOF part, just figured out that directly using ifstream for a condition check will trigger the bool operator that will return false if eof is hit, and cause the loop to break.
2 年多之前 回复
weixin_41568174
from.. If you're reading in a way that doesn't "read ahead", like just getting individual characters out, then you never trigger EOF until you actually step over the end of the stream: wandbox.org/permlink/oFaYFTFtnEfucaMv
2 年多之前 回复
weixin_41568174
from.. the stream reaches EOF while reading digits to form the last element. It is in the next round that there are no digits left, and the attempt to read past the end of the stream makes the stream "fail", which is the exit condition for the loop. Here's a demo.
2 年多之前 回复
csdnceshi80
胖鸭 One clarification please, in the first approach, as i understand, >> returns the reference to the stream object. So the question is when the stream reaches eof what will be returned that makes the while loop break.
2 年多之前 回复
weixin_41568174
from.. You can't do it in the first approach, which assumes you're parsing tokens and doesn't know what a "line" is. It's trivial in the second approach, where you just check the first character of the line string (potentially skipping whitespace).
接近 3 年之前 回复
csdnceshi71
Memor.の What's the best way to skip "#" commented lines use the first or second approach? Thanks.
接近 3 年之前 回复
csdnceshi54
hurriedly% enjoy your cold one
接近 3 年之前 回复
weixin_41568174
from.. thatched :-)
接近 3 年之前 回复
csdnceshi54
hurriedly% peasant quest command make friends with @KerrekSB
接近 3 年之前 回复
csdnceshi50
三生石@ For an explanation of the while(getline(f, line)) { } construct and regarding error handling please have a look at this (my) article: gehrcke.de/2011/06/… (I think I do not need to have bad conscience posting this here, it even slightly pre-dates this answer).
5 年多之前 回复
csdnceshi51
旧行李 Huh. I was wrong. I didn't know it could do that. I might have some code of my own to rewrite.
5 年多之前 回复
weixin_41568174
from.. Are you sure?
5 年多之前 回复
csdnceshi51
旧行李 That would only work if the comma was surrounded by spaces, i.e., "1 , 2". If the line contained "1,2", then your code would try to convert "1,2" into an integer (storing it in a) while c and b would get the tokens/delimiters on the next line. With anything besides whitespace delimiters, you really need to use std::getline() and parse the line.
5 年多之前 回复
weixin_41568174
from.. Ah, so when you said "token" you meant "delimiter". Right. With a comma, you'd say: int a, b; char c; while ((infile >> a >> c >> b) && (c == ','))
接近 6 年之前 回复
weixin_41568127
?yb? the OP used a space to delimit the two integers. I wanted to know if while (infile >> a >> b) would work if the OP used a as a comma a delimiter, because that is the scenario in my own program
接近 6 年之前 回复
weixin_41568174
from.. I don't understand what "commas as the token" means. Commas don't represent integers.
接近 6 年之前 回复
weixin_41568127
?yb? Will solution #1 work with commas as the token?
接近 6 年之前 回复

Use ifstream to read data from a file:

std::ifstream input( "filename.ext" );

If you really need to read line by line, then do this:

for( std::string line; getline( input, line ); )
{
    ...for each line in input...
}

But you probably just need to extract coordinate pairs:

int x, y;
input >> x >> y;

Update:

In your code you use ofstream myfile;, however the o in ofstream stands for output. If you want to read from the file (input) use ifstream. If you want to both read and write use fstream.

weixin_41568134
MAO-EYE getline is in string see, so don't forget the #include <string>
大约 3 年之前 回复
weixin_41568208
北城已荒凉 Your solution is a bit improved: your line variable is not visible after file read-in in contrast to Kerrek SB's second solution which is good and simple solution too.
大约 7 年之前 回复

Since your coordinates belong together as pairs, why not write a struct for them?

struct CoordinatePair
{
    int x;
    int y;
};

Then you can write an overloaded extraction operator for istreams:

std::istream& operator>>(std::istream& is, CoordinatePair& coordinates)
{
    is >> coordinates.x >> coordinates.y;

    return is;
}

And then you can read a file of coordinates straight into a vector like this:

#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
}
csdnceshi61
derek5. in the operator>> it is more correct to say is >> std::ws >> coordinates.x >> std::ws >> coordinates.y >> std::ws; since otherwise you are assuming that your input stream is in the whitespace-skipping mode.
3 年多之前 回复
csdnceshi80
胖鸭 If it's not possible to read two int tokens, then the is stream will evaluate to false and the reading loop will terminate at that point. You can detect this within operator>> by checking the return value of the individual reads. If you want to roll back the stream, you would call is.clear().
3 年多之前 回复
csdnceshi70
笑故挽风 What happens when it's not possible to read two int tokens from the stream in operator>>? How can one make it work with a backtracking parser (i.e. when operator>> fails, roll back the stream to previous position end return false or something like that)?
3 年多之前 回复

Reading a file line by line in C++ can be done in some different ways.

[Fast] Loop with std::getline()

The simplest approach is to open an std::ifstream and loop using std::getline() calls. The code is clean and easy to understand.

#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();
}

[Fast] Use Boost's file_description_source

Another possibility is to use the Boost library, but the code gets a bit more verbose. The performance is quite similar to the code above (Loop with 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();
        }
    }
}

[Fastest] Use C code

If performance is critical for your software, you may consider using the C language. This code can be 4-5 times faster than the C++ versions above, see benchmark below

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);

Benchmark -- Which one is faster?

I have done some performance benchmarks with the code above and the results are interesting. I have tested the code with ASCII files that contain 100,000 lines, 1,000,000 lines and 10,000,000 lines of text. Each line of text contains 10 words in average. The program is compiled with -O3 optimization and its output is forwarded to /dev/null in order to remove the logging time variable from the measurement. Last, but not least, each piece of code logs each line with the printf() function for consistency.

The results show the time (in ms) that each piece of code took to read the files.

The performance difference between the two C++ approaches is minimal and shouldn't make any difference in practice. The performance of the C code is what makes the benchmark impressive and can be a game changer in terms of speed.

                             10K lines     100K lines     1000K lines
Loop with std::getline()         105ms          894ms          9773ms
Boost code                       106ms          968ms          9561ms
C code                            23ms          243ms          2397ms

enter image description here

csdnceshi54
hurriedly% Groovy. Thanks. Wonder where the slowdown is.
大约 2 年之前 回复
weixin_41568174
from.. Thanks for bringing this concern. I've redone the tests and the performance is still the same. I have edited the code to use the printf() function in all cases for consistency. I have also tried using std::cout in all cases and this made absolutely no difference. As I have just described in the text, the output of the program goes to /dev/null so the time to print the lines is not measured.
大约 2 年之前 回复
csdnceshi54
hurriedly% What happens if you remove C++'s synchronization with C on the console outputs? You might be measuring a known disadvantage of the default behavior of std::cout vs printf.
大约 2 年之前 回复

Expanding on the accepted answer, if the input is:

1,NYC
2,ABQ
...

you will still be able to apply the same logic, like this:

#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();

Although there is no need to close the file manually but it is good idea to do so if the scope of the file variable is bigger:

    ifstream infile(szFilePath);

    for (string line = ""; getline(infile, line); )
    {
        //do something with the line
    }

    if(infile.is_open())
        infile.close();

with command line arguments:

#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
#include "print.h"

using namespace std;

int main (int argc, char *argv[]) 
{
    vector<string> list;
    ifstream in_stream;
    string line;
    in_stream.open(argv[1]);

    while(!in_stream.eof())
    {
        in_stream >> line;
        list.push_back(line);
    }
    in_stream.close();
    print(list);
    sort(list.begin(), list.end());
    print(list);
}
csdnceshi70
笑故挽风 On top of that this doesn't even answer the question. It doesn't process the data line by line and doesn't view each line as a set of coordinates.
大约 5 年之前 回复
csdnceshi70
笑故挽风 Please fix the broken loop on eof(): stackoverflow.com/questions/5605125/… In addition to looping on eof() you don't bother to check if your read succeeds before putting its value in the vector.
大约 5 年之前 回复
weixin_41568184
叼花硬汉 To explain while(!in_stream.eof()): say the last line contains "12 4\n" (per question), when your loop's >> line reads "4", it will recognise the \n as white-space delimiting the value "4" and stop with the input position at that final newline, but will not yet have tried to go beyond the newline; eof() will not have been set. So, when your while loop tests eof() it thinks there's more input, calls in_stream >> line; again but you don't test for the failure: in C++03 line is unspecified, in C++11 I think "". Either way, you push_back one repeat or garbage string.
大约 5 年之前 回复
csdnceshi53
Lotus@ I would suggest using getline here also. The above delimits the input based on any whitespaces which may not be desired while getline() has default delimiter '\n'
大约 6 年之前 回复
weixin_41568208
北城已荒凉 Good, but it will fail in many other cases. It's better to do while (in_stream >> line).
6 年多之前 回复
csdnceshi58
Didn"t forge compiled and ran on windows Visual Studio
6 年多之前 回复
weixin_41568208
北城已荒凉 while (!file.eof()) is wrong.
接近 7 年之前 回复
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐