C ++でifstreamを使用して1行ずつファイルを読み取る


612

file.txtの内容は次のとおりです。

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

5 3座標ペアはどこですか。C ++でこのデータを1行ずつ処理するにはどうすればよいですか?

最初の行を取得できますが、ファイルの次の行を取得するにはどうすればよいですか?

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

回答:


916

最初に、ifstream

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

2つの標準的な方法は次のとおりです。

  1. すべての行が2つの数値で構成されていて、トークンごとにトークンを読み取ると仮定します。

    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、2014

8
OPは、2つの整数を区切るためにスペースを使用しました。OPがコンマとして区切り文字として使用されている場合、while(infile >> a >> b)が機能するかどうかを知りたかったのです。これは、私のプログラムのシナリオです
Edward Karak

30
@EdwardKarak:ああ、「トークン」と言ったときは「区切り文字」を意味していました。正しい。コンマを使用すると、次のようになりますint a, b; char c; while ((infile >> a >> c >> b) && (c == ','))
。– Kerrek SB 2014

11
@KerrekSB:ええと。私は間違っていた。そんなことができるとは知りませんでした。自分で書き直したいコードがあるかもしれません。
Mark H

4
while(getline(f, line)) { }コンストラクトの説明とエラー処理については、この(私の)記事をご覧ください:gehrcke.de/2011/06/… (これをここに投稿することは、私が悪い良心を持っている必要はないと思います。この回答の日付)。
Jan-Philip Gehrcke博士、2015年

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;、ただしoin ofstreamはを表しoutputます。ファイル(入力)から読み取る場合は、を使用しますifstream。読み取りと書き込みの両方を行う場合は、を使用してくださいfstream


8
ソリューションは少し改善されています。KerrekSBの2番目のソリューションであるシンプルで優れたソリューションとは対照的に、ファイルの読み込み後に行変数は表示されません。
DanielTuzes 2013

3
getlineであるstring 参照してくださいので、忘れないでください、#include <string>
mxmlnkn

55

C ++でファイルを1行ずつ読み取るには、いくつかの方法があります。

[高速] 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のfile_description_sourceを使用する

もう1つの可能性は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言語の使用を検討してください。このコードは、上記の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()なことですが、一貫性を保つために、コードの各部分が関数を使用して各行を記録します。

結果は、各コードがファイルを読み取るのにかかった時間(ミリ秒)を示します。

2つの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::coutvsのデフォルト動作の既知の欠点を測定している可能性がありますprintf
user4581301 2018

2
この懸念をお寄せいただきありがとうございます。テストをやり直しましたが、パフォーマンスは同じです。printf()一貫性を保つため、すべての場合に関数を使用するようにコードを編集しました。私もstd::coutすべてのケースで使用してみましたが、これはまったく違いがありませんでした。テキストで説明したように、プログラムの出力はに行く/dev/nullため、行を印刷する時間は測定されません。
HugoTeixeira 2018

6
Groovy。ありがとう。減速はどこにあるのだろう。
user4581301 2018

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

次に、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
}

1
intストリームから2つのトークンを読み取ることができない場合はoperator>>どうなりますか?どのようにしてバックトラッキングパーサーで動作させることができますか(つまり、operator>>失敗した場合、ストリームを前の位置にロールバックして、falseを返すなど)。
fferri

2つのintトークンを読み取ることができない場合、isストリームは評価されfalse、読み取りループはその時点で終了します。これoperator>>は、個々の読み取りの戻り値を確認することで検出できます。ストリームをロールバックする場合は、を呼び出しますis.clear()
マーティンブロードハースト2017年

operator>>、言うことがより正確であるis >> std::ws >> coordinates.x >> std::ws >> coordinates.y >> std::ws;そうでない場合は、あなたの入力ストリームが空白・スキップ・モードであると仮定しているため。
Darko Veberic 2017年

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は、各行を取得する方法を求めました。この答えはそれを行い、ファイルを確実に閉じるための素晴らしいヒントを提供します。単純なプログラムの場合は必要ないかもしれませんが、最低でも素晴らしい習慣を作る必要があります。数行のコードを追加して、それが取得する個々の行を処理することで改善される可能性がありますが、全体的にはOPの質問に対する最も簡単な答えです。
Xandor

2

この答えは、Visual Studio 2017で、コンパイルされたコンソールアプリケーションに関連する場所をテキストファイルから読み取る場合です。

まず、テキストファイル(この場合はtest.txt)をソリューションフォルダーに配置します。コンパイル後、テキストファイルをapplicationName.exeと同じフォルダーに保存します。

C:\ Users \ "username" \ 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
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.