C ++ 'da ifstream kullanarak dosya satır satır okuma


612

File.txt dosyasının içeriği:

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

5 3Bir koordinat çifti nerede . Bu veriyi C ++ 'da satır satır nasıl işleyebilirim?

İlk satırı alabilirim, ancak dosyanın sonraki satırını nasıl alabilirim?

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

Yanıtlar:


916

İlk önce aşağıdakileri yapın ifstream:

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

İki standart yöntem şunlardır:

  1. Her satırın iki sayıdan oluştuğunu ve jetonla jetonu okuduğunu varsayın:

    int a, b;
    while (infile >> a >> b)
    {
        // process pair (a,b)
    }
    
  2. Dize akışlarını kullanarak satır tabanlı ayrıştırma:

    #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) ve (2) 'yi karıştırmamalısınız, çünkü jeton tabanlı ayrıştırma yeni satırları kaldırmaz, bu nedenle getline()jeton tabanlı ekstraksiyon sizi bir hattı zaten.


1
@EdwardKarak: "Simge olarak virgül" ne anlama geldiğini anlamıyorum. Virgül tamsayıları temsil etmez.
Kerrek SB

8
OP iki tamsayıyı sınırlamak için bir boşluk kullandı. OP'nin bir sınırlayıcı olarak virgül olarak kullanması durumunda (infile >> a >> b) işe yarayıp yaramayacağını bilmek istedim, çünkü bu kendi programımdaki senaryo
Edward Karak

30
@EdwardKarak: Ah, "jeton" dediğinde "sınırlayıcı" demek istedin. Sağ. Virgül ile şunu söylerdiniz:int a, b; char c; while ((infile >> a >> c >> b) && (c == ','))
Kerrek SB

11
@KerrekSB: Ha. Ben hatalıydım. Bunu yapabileceğini bilmiyordum. Yeniden yazmak için kendi kodum olabilir.
Mark H

4
while(getline(f, line)) { }Yapının açıklaması ve hataların ele alınması ile ilgili olarak lütfen bu (benim) makaleye bir göz atın: gehrcke.de/2011/06/… (Sanırım bunu burada göndermek için vicdanımın olması gerekmiyor, hatta biraz daha bu yanıtı tarihlendirir).
Dr.Jan-Philip Gehrcke

175

ifstreamBir dosyadan veri okumak için kullanın :

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

Gerçekten satır satır okumaya ihtiyacınız varsa, bunu yapın:

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

Ama muhtemelen sadece koordinat çiftlerini çıkarmanız gerekir:

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

Güncelleme:

Kodunuzda kullanın ofstream myfile;, ancak oin ofstreamanlamına gelir output. Dosyadan (giriş) okumak istiyorsanız kullanın ifstream. Hem okumak hem de yazmak istiyorsanız kullanın fstream.


8
Çözümünüz biraz geliştirildi: Kerrek SB'nin ikinci çözümünün aksine dosya okuduktan sonra satır değişkeniniz iyi ve basit bir çözüm.
DanielTuzes

3
getlineiçinde olduğu string See , böylece unutma#include <string>
mxmlnkn

55

C ++ 'da bir dosyayı satır satır okumak bazı farklı yollarla yapılabilir.

[Fast] Döngü ile std :: getline ()

En basit yaklaşım std :: getstream () çağrılarını kullanarak bir std :: ifstream ve loop'u açmaktır. Kod temiz ve anlaşılması kolaydır.

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

[Hızlı] Boost'un file_description_source dosyasını kullanın

Başka bir olasılık Boost kütüphanesini kullanmaktır, ancak kod biraz daha ayrıntılı hale gelir. Performans yukarıdaki koda oldukça benzerdir (std :: getline () ile döngü).

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

[En hızlı] C kodunu kullanın

Yazılımınız için performans kritikse, C dilini kullanmayı düşünebilirsiniz. Bu kod yukarıdaki C ++ sürümlerinden 4-5 kat daha hızlı olabilir, aşağıdaki karşılaştırmaya bakın

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

Deney - Hangisi daha hızlı?

Yukarıdaki kod ile bazı performans kriterleri yaptım ve sonuçlar ilginç. 100.000 satır, 1.000.000 satır ve 10.000.000 satır metin içeren ASCII dosyaları ile kodu test ettim. Her metin satırı ortalama 10 kelime içerir. Program -O3optimizasyon ile derlenir ve çıktısı /dev/nullkayıt süresi değişkenini ölçümden kaldırmak için yönlendirilir . Son olarak, her bir kod parçası, her satırı printf()tutarlılık işleviyle günlüğe kaydeder .

Sonuçlar, her kod parçasının dosyaları okumak için aldığı süreyi (ms cinsinden) gösterir.

İki C ++ yaklaşımı arasındaki performans farkı minimaldir ve uygulamada herhangi bir fark yaratmamalıdır. C kodunun performansı, ölçütü etkileyici kılan şeydir ve hız açısından bir oyun değiştirici olabilir.

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

resim açıklamasını buraya girin


1
Konsol çıkışlarında C ++ 'nin C ile senkronizasyonunu kaldırırsanız ne olur? Sen varsayılan davranışı nedeniyle bilinen bir dezavantaj ölçme olabilir std::coutvs printf.
user4581301

2
Bu endişeyi getirdiğin için teşekkürler. Testleri yeniden yaptım ve performans hala aynı. Ben printf()tutarlılık için her durumda işlevi kullanmak için kodu düzenledim . Ayrıca std::couther durumda kullanmayı denedim ve bu kesinlikle hiçbir fark yaratmadı. Metinde az önce açıkladığım gibi, programın çıktısı gider, /dev/nullböylece satırları yazdırma süresi ölçülmez.
HugoTeixeira

6
Groovy. Teşekkürler. Yavaşlamanın nerede olduğunu merak et.
user4581301

4
Merhaba @HugoTeixeira Bunun eski bir konu olduğunu biliyorum, sonuçlarınızı çoğaltmaya çalıştım ve c ve c ++ arasında önemli bir fark göremedim github.com/simonsso/readfile_benchmarks
Simson

Varsayılan olarak, C ++ giriş akışları ile senkronize edilir cstdio. Ayarı denemeliydiniz std::ios_base::sync_with_stdio(false). Sanırım çok daha iyi performanslar elde edersiniz ( Senkronizasyon kapatıldığında uygulama tanımlı olduğu için garanti edilmez ).
Fareanor

11

Koordinatlarınız çift olarak bulunduğundan, neden onlar için bir yapı yazmıyorsunuz?

struct CoordinatePair
{
    int x;
    int y;
};

Sonra istreams için aşırı yüklenmiş bir çıkarma operatörü yazabilirsiniz:

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

    return is;
}

Ve sonra bir koordinat dosyasını doğrudan şöyle bir vektöre okuyabilirsiniz:

#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
intAkıştaki iki jetonu okumak mümkün olmadığında ne olur operator>>? Bir geri izleme ayrıştırıcısı ile nasıl çalışabilir (yani operator>>başarısız olduğunda, akışı önceki konum sonuna geri döndürmek yanlış veya bunun gibi bir şey)?
fferri

İki intjetonu okumak mümkün değilse , isakış değerlendirilir falseve okuma döngüsü bu noktada sona erer. operator>>Bireysel okumaların dönüş değerini kontrol ederek bunu içinde tespit edebilirsiniz . Akışı geri almak isterseniz arama yaparsınız is.clear().
Martin Broadhurst

içinde operator>>söylemek daha doğru olur is >> std::ws >> coordinates.x >> std::ws >> coordinates.y >> std::ws;Eğer giriş akışı boşluk-atlama modunda olduğunu varsayıyoruz aksi beri.
Darko Veberic

7

Girdi şu ise, kabul edilen cevabı genişletmek:

1,NYC
2,ABQ
...

yine de aynı mantığı şu şekilde uygulayabileceksiniz:

#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

Dosyayı manuel olarak kapatmaya gerek olmamasına rağmen, dosya değişkeninin kapsamı daha büyükse bunu yapmak iyi bir fikirdir:

    ifstream infile(szFilePath);

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

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

Bunun aşağı oyu hak ettiğinden emin değilim. OP her satırı almanın bir yolunu istedi. Bu yanıt bunu yapar ve dosyanın kapatıldığından emin olmak için harika bir ipucu verir. Basit bir program için gerekli olmayabilir, ancak en azından oluşturmak için BÜYÜK bir alışkanlık. Çektiği münferit satırları işlemek için birkaç kod satırı ekleyerek geliştirilebilir, ancak genel olarak OP sorusuna en basit cevaptır.
Xandor

2

Bu cevap visual studio 2017 içindir ve derlenmiş konsol uygulamanıza göre hangi konumun metin dosyasından okumak istiyorsanız.

önce metin dosyanızı (bu durumda test.txt) çözüm klasörünüze koyun. Derledikten sonra metin dosyasını applicationName.exe ile aynı klasörde tut

C: \ Users \ "kullanıcı adı" \ kaynak \ repo \ "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

Bu, bir C ++ programına veri yüklemek için genel bir çözümdür ve readline işlevini kullanır. Bu CSV dosyaları için değiştirilebilir, ancak sınırlayıcı burada bir boşluktur.

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
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.