Читайте файл построчно, используя ifstream в C ++


612

Содержимое файла file.txt:

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

Где 5 3пара координат. Как мне обрабатывать эти данные построчно в C ++?

Я могу получить первую строку, но как мне получить следующую строку файла?

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

Ответы:


916

Сначала сделайте ifstream:

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

Два стандартных метода:

  1. Предположим, что каждая строка состоит из двух чисел и считывает токен токеном:

    int a, b;
    while (infile >> a >> b)
    {
        // process pair (a,b)
    }
    
  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)
    }
    

Вы не должны смешивать (1) и (2), так как синтаксический анализ на основе токенов не сожирает новые строки, поэтому вы можете получить ложные пустые строки, если будете использовать getline()после того, как извлечение на основе токенов привело вас к концу линия уже.


1
@EdwardKarak: я не понимаю, что означает «запятая как знак». Запятые не представляют целые числа.
— Kerrek SB

8
ОП использовал пробел для разделения двух целых чисел. Я хотел знать, сработает ли while (infile >> a >> b), если OP использует a как запятую в качестве разделителя, потому что это сценарий в моей собственной программе
— Эдвард Карак

30
@EdwardKarak: Ах, когда вы сказали «токен», вы имели в виду «разделитель». Правильно. С запятой вы скажете:int a, b; char c; while ((infile >> a >> c >> b) && (c == ','))
— Kerrek SB

11
@KerrekSB: Да. Я был неправ. Я не знал, что это может сделать это. Я мог бы иметь свой собственный код, чтобы переписать.
— Марк Х

4
Для объяснения while(getline(f, line)) { }конструкции и относительно обработки ошибок, пожалуйста, взгляните на эту (мою) статью: gehrcke.de/2011/06/… (я думаю, что мне не нужно с совестью публиковать это здесь, это даже немного предварительно даты этого ответа).
— Доктор Ян-Филипп Герке

175

Используйте 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;, однако oв ofstreamозначает output. Если вы хотите прочитать из файла (вход), используйте ifstream. Если вы хотите и читать, и писать, используйте fstream.


8
Ваше решение немного улучшено: ваша строковая переменная не видна после чтения файла, в отличие от второго решения Kerrek SB, которое также является хорошим и простым решением.
— DanielTuzes

3
getlineнаходится в string Престоле , так что не забывайте#include <string>
— mxmlnkn

56

Чтение файла строка за строкой в ​​C ++ может быть сделано несколькими различными способами.

[Быстрый] Цикл с std :: getline ()

Самый простой подход - открыть 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's file_description_source

Другая возможность - использовать библиотеку 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 раз быстрее, чем версии C ++ выше, см. Тест ниже

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

Тест - Какой из них быстрее?

Я сделал несколько тестов производительности с кодом выше, и результаты интересны. Я проверил код с файлами ASCII, которые содержат 100 000 строк, 1 000 000 строк и 10 000 000 строк текста. Каждая строка текста содержит в среднем 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

введите описание изображения здесь


1
Что произойдет, если вы удалите синхронизацию C ++ с C на выходах консоли? Вы можете измерить известный недостаток поведения по умолчанию std::coutпротив printf.
— user4581301

2
Спасибо за то, что принесли это беспокойство. Я переделал тесты и производительность осталась прежней. Я отредактировал код, чтобы использовать printf()функцию во всех случаях для согласованности. Я также пытался использовать std::coutво всех случаях, и это не имело никакого значения. Как я только что описал в тексте, выходные данные программы идут /dev/nullтак, что время для печати строк не измеряется.
— HugoTeixeira

6
Groovy. Спасибо. Интересно, где замедление.
— user4581301

4
Привет @HugoTeixeira. Я знаю, что это старая ветка, я пытался воспроизвести ваши результаты и не увидел существенной разницы между c и c ++. Github.com/simonsso/readfile_benchmarks
— Simson

По умолчанию входящие и исходящие потоки C ++ синхронизируются с cstdio. Вы должны были попробовать с настройкой std::ios_base::sync_with_stdio(false). Я предполагаю, что вы получили бы намного лучшие результаты (хотя это не гарантировано, поскольку это определяется реализацией, когда синхронизация отключена).
— Fareanor

11

Поскольку ваши координаты принадлежат парам, почему бы не написать для них структуру?

struct CoordinatePair
{
    int x;
    int y;
};

Затем вы можете написать перегруженный оператор извлечения для istreams:

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
}

1
Что происходит, когда невозможно прочитать два intтокена из потока operator>>? Как можно заставить его работать с анализатором обратного отслеживания (т. operator>>Е. Когда происходит сбой, откатить поток до предыдущей позиции и вернуть значение false или что-то в этом роде)?
— февраля

Если невозможно прочитать два intтокена, тогда isпоток оценивается falseи цикл чтения завершается в этой точке. Вы можете обнаружить это operator>>, проверив возвращаемое значение отдельных чтений. Если вы хотите откатить поток, вы бы позвонили is.clear().
— Мартин Бродхерст

в operator>>правильнее сказать, is >> std::ws >> coordinates.x >> std::ws >> coordinates.y >> std::ws;так как в противном случае вы предполагаете, что ваш входной поток находится в режиме пропуска пробелов.
— Дарко Веберик

7

Расширение на принятый ответ, если ввод:

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

2

Хотя нет необходимости закрывать файл вручную, но лучше сделать это, если область действия переменной файла больше:

    ifstream infile(szFilePath);

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

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

Не уверен, что это заслуживает отрицательного голосования. ОП попросил способ получить каждую строку. Этот ответ делает это и дает отличный совет, чтобы убедиться, что файл закрывается. Для простой программы это может и не понадобиться, но как минимум ОТЛИЧНАЯ привычка формироваться. Возможно, это можно улучшить, добавив несколько строк кода для обработки отдельных строк, которые он извлекает, но в целом это самый простой ответ на вопрос OP.
— Ксандор

2

Этот ответ предназначен для Visual Studio 2017, и если вы хотите прочитать из текстового файла, какое расположение относительно вашего скомпилированного консольного приложения.

сначала поместите ваш текстовый файл (в данном случае test.txt) в папку вашего решения. После компиляции сохраните текстовый файл в той же папке с applicationName.exe

C: \ Users \ "Имя пользователя" \ Source \ Repos \ "solutionName" \ "solutionName"

#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

Это общее решение для загрузки данных в программу на C ++ с использованием функции readline. Это может быть изменено для файлов CSV, но разделитель здесь - пробел.

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
}
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.