使用C++中的ifstream逐行读取文件

duk*_*vin 580 c++ file-io ofstream

file.txt的内容是:

5 3
6 4
7 1
10 5
11 6
12 3
12 4
Run Code Online (Sandbox Code Playgroud)

5 3坐标对在哪里.如何在C++中逐行处理此数据?

我能够得到第一行,但是如何获得文件的下一行?

ifstream myfile;
myfile.open ("text.txt");
Run Code Online (Sandbox Code Playgroud)

Ker*_* SB 871

首先,做一个ifstream:

#include <fstream>
std::ifstream infile("thefile.txt");
Run Code Online (Sandbox Code Playgroud)

两种标准方法是:

  1. 假设每行包含两个数字并按令牌读取令牌:

    int a, b;
    while (infile >> a >> b)
    {
        // process pair (a,b)
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 基于行的解析,使用字符串流:

    #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)
    }
    
    Run Code Online (Sandbox Code Playgroud)

你不应该混合使用(1)和(2),因为基于令牌的解析不会吞噬新行,所以如果你getline()在基于令牌的提取之后使用,你最终会得到虚假的空行.已经排队了.

  • @EdwardKarak:啊,所以当你说"令牌"时你的意思是"分隔符".对.用逗号,你会说:`int a,b; char c; while((infile >> a >> c >> b)&&(c ==','))` (28认同)
  • 解决方案#1是否可以使用逗号作为令牌? (13认同)
  • @KerrekSB:嗯.我错了.我不知道它能做到这一点.我可能有一些我自己的代码要重写. (9认同)
  • OP使用空格来划分两个整数.如果OP使用逗号作为逗号分隔符,我想知道while(infile >> a >> b)是否有效,因为这是我自己程序中的场景 (7认同)
  • 有关`while(getline(f,line)){}`结构和错误处理的解释,请查看这篇(我的)文章:http://gehrcke.de/2011/06/reading-files- in-c-using-ifstream-dealing-correct-with-badbit-failbit-eofbit-and-perror /(我想我不需要在这里发布不良信息,它甚至会在这个答案之前略微提前). (4认同)

K-b*_*llo 165

用于ifstream从文件中读取数据:

std::ifstream input( "filename.ext" );
Run Code Online (Sandbox Code Playgroud)

如果你真的需要逐行阅读,那么这样做:

for( std::string line; getline( input, line ); )
{
    ...for each line in input...
}
Run Code Online (Sandbox Code Playgroud)

但您可能只需要提取坐标对:

int x, y;
input >> x >> y;
Run Code Online (Sandbox Code Playgroud)

更新:

在你使用的代码中ofstream myfile;,但oin ofstream代表output.如果要从文件中读取(输入)使用ifstream.如果你想同时读写使用fstream.

  • 您的解决方案有所改进:文件读入后您的行变量不可见,与Kerrek SB的第二个解决方案相比,这也是一个好的简单解决方案. (7认同)
  • `getline`在`string` [see](http://www.cplusplus.com/reference/string/string/getline/)中,所以不要忘记`#include <string>` (3认同)

Hug*_*ira 43

在C++中逐行读取文件可以通过某些不同的方式完成.

[快]循环使用std :: getline()

最简单的方法是使用std :: getline()调用打开std :: ifstream和循环.代码简洁易懂.

#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();
}
Run Code Online (Sandbox Code Playgroud)

[Fast]使用Boost的file_description_source

另一种可能性是使用Boost库,但代码更加冗长.性能与上面的代码非常相似(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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

[最快]使用C代码

如果性能对您的软件至关重要,您可以考虑使用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);
Run Code Online (Sandbox Code Playgroud)

基准 - 哪一个更快?

我已经使用上面的代码完成了一些性能基准测试,结果很有趣.我已经使用包含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
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 请注意,您在 C 中使用的 `getline` 是一个 gnu 扩展(现已添加到 POSIX)。它不是标准 C 函数。 (6认同)
  • Groovy的.谢谢.想知道减速的地方. (5认同)
  • 如果在控制台输出上删除 C++ 与 C 的同步会发生什么?您可能正在测量 `std::cout` 与 `printf` 的默认行为的一个已知缺点。 (4认同)
  • 谢谢你提出这个问题.我重做测试,性能仍然相同.我已编辑代码以在所有情况下使用`printf()`函数以保持一致性.在所有情况下我也尝试过使用`std :: cout`,这完全没有区别.正如我在文中所描述的那样,程序的输出转到`/ dev/null`,因此不会测量打印行的时间. (2认同)
  • 嗨@HugoTeixeira我知道这是一个旧线程,我试图复制您的结果,但看不到c和c ++之间的任何显着差异https://github.com/simonsso/readfile_benchmarks (2认同)

Mar*_*rst 11

由于你的坐标是成对的,为什么不为它们写一个结构?

struct CoordinatePair
{
    int x;
    int y;
};
Run Code Online (Sandbox Code Playgroud)

然后你可以为istreams编写一个重载的提取运算符:

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

    return is;
}
Run Code Online (Sandbox Code Playgroud)

然后你可以直接将坐标文件读入这样的矢量:

#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
}
Run Code Online (Sandbox Code Playgroud)

  • 当无法从“operator&gt;&gt;”中的流中读取两个“int”标记时会发生什么?如何让它与回溯解析器一起工作(即当“operator&gt;&gt;”失败时,将流回滚到前一个位置 end return false 或类似的东西)? (2认同)

gsa*_*ras 7

扩展已接受的答案,如果输入是:

1,NYC
2,ABQ
...
Run Code Online (Sandbox Code Playgroud)

您仍然可以应用相同的逻辑,如下所示:

#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();
Run Code Online (Sandbox Code Playgroud)


Uni*_*sus 6

这个答案适用于 Visual Studio 2017,如果您想从文本文件中读取哪个位置相对于您编译的控制台应用程序。

首先将文本文件(本例中为 test.txt)放入解决方案文件夹中。编译后将文本文件保留在与 applicationName.exe 相同的文件夹中

C:\Users\"用户名"\source\repos\"解决方案名称"\"解决方案名称"

#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();
}
Run Code Online (Sandbox Code Playgroud)


小智 5

虽然不需要手动关闭文件,但如果文件变量的范围较大,最好这样做:

    ifstream infile(szFilePath);

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

    if(infile.is_open())
        infile.close();
Run Code Online (Sandbox Code Playgroud)