Читайте файл построчно, используя 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.

K-Балло
источник
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

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

HugoTeixeira
источник
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();
gsamaras
источник
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();
}
Universus
источник
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
}
mjr2000
источник