2011-01-13 7 views
3

Guido Van Rossum zeigt die Einfachheit von Python in diesem article und die Nutzung dieser Funktion macht für liest aus einer Datei unbekannter Länge gepuffert:Äquivalent eines Python-Generator in C++ für gepufferte liest

def intsfromfile(f): 
    while True: 
     a = array.array('i') 
     a.fromstring(f.read(4000)) 
     if not a: 
      break 
     for x in a: 
      yield x 

Ich muss mach das gleiche in C++ aus Geschwindigkeitsgründen! Ich habe viele Dateien mit sortierten Listen von 64-Bit-Ganzzahlen ohne Vorzeichen, die ich zusammenführen muss. Ich habe dieses schöne Stück von code zum Zusammenführen von Vektoren gefunden.

Ich stecke, wie ein ifstream für eine Datei von unbekannter Länge selbst als Vektor präsentieren zu machen, die glücklich über laufen werden können, bis das Ende der Datei erreicht ist. Irgendwelche Vorschläge? Banne ich den richtigen Baum mit einem istreambuf_iterator?

+0

Sind die ganzen Zahlen in der Datei durch Zeilenumbrüche, Leerzeichen usw. getrennt? Wenn ja, ist der 'istream_iterator' dein Freund. – Dawson

+0

@Toolbox Danke für die Antwort und die Lösung!Das Format der Dateien ist binäre, 64-Bit-Little-Endian-Ganzzahlen ohne Vorzeichen ohne Trennzeichen. Sieht also aus wie istreambuf_iterator ist der Weg zu gehen? Kannst du helfen, istrebbuf_iterator als Vektor zu tarnen? – Donny

+0

Sicher! Gib mir ein paar Momente, um etwas hochzupeitschen (und stelle sicher, dass es kompiliert wird). – Dawson

Antwort

7

Um einen ifstream (oder wirklich jeden beliebigen Eingabestream) in einem Formular zu tarnen, das wie ein Iterator funktioniert, möchten Sie die Vorlagenklasse istream_iterator oder istreambuf_iterator verwenden. Ersteres ist nützlich für Dateien, bei denen die Formatierung von Bedeutung ist. Zum Beispiel kann eine Datei voller Leerzeichen getrennte ganze Zahlen in den Iterator Bereich Konstrukteurs-Vektor wie folgt gelesen werden:

#include <fstream> 
#include <vector> 
#include <iterator> // needed for istream_iterator 

using namespace std; 

int main(int argc, char** argv) 
{ 
    ifstream infile("my-file.txt"); 

    // It isn't customary to declare these as standalone variables, 
    // but see below for why it's necessary when working with 
    // initializing containers. 
    istream_iterator<int> infile_begin(infile); 
    istream_iterator<int> infile_end; 

    vector<int> my_ints(infile_begin, infile_end); 

    // You can also do stuff with the istream_iterator objects directly: 
    // Careful! If you run this program as is, this won't work because we 
    // used up the input stream already with the vector. 

    int total = 0; 
    while (infile_begin != infile_end) { 
     total += *infile_begin; 
     ++infile_begin; 
    } 

    return 0; 
} 

istreambuf_iterator durch lesen verwendet wird, um Dateien ein einzelnes Zeichen in einer Zeit, die Formatierung der Eingabe ohne Berücksichtigung . Das bedeutet, dass Sie alle Zeichen einschließlich Leerzeichen, Zeilenumbrüche usw. zurückgeben. Abhängig von Ihrer Anwendung ist dies möglicherweise sinnvoller.

Hinweis: Scott Meyers erklärt in Wirksame STL warum die separaten Variablen Deklarationen für istream_iterator oben benötigt werden. Normalerweise würden Sie so etwas tun:

ifstream infile("my-file.txt"); 
vector<int> my_ints(istream_iterator<int>(infile), istream_iterator<int>()); 

jedoch C++ parst tatsächlich die zweite Zeile in einer unglaublich bizarre Art und Weise. Sie sieht es als Deklaration einer Funktion namens my_ints, die zwei Parameter akzeptiert und eine vector<int> zurückgibt. Der erste Parameter ist vom Typ istream_iterator<int> und heißt infile (die Klammern werden ignoriert). Der zweite Parameter ist ein Funktionszeiger ohne Namen, der (aufgrund der Klammern) null Argumente annimmt und ein Objekt vom Typ istream_iterator<int> zurückgibt.

Ziemlich cool, aber auch ziemlich ärgerlich, wenn Sie nicht darauf achten.


EDIT

Hier ist ein Beispiel für die istreambuf_iterator Verwendung in einer Datei von 64-Bit-Zahlen gelegt lesen End-to-end:

#include <fstream> 
#include <vector> 
#include <algorithm> 
#include <iterator> 

using namespace std; 

int main(int argc, char** argv) 
{ 
    ifstream input("my-file.txt"); 
    istreambuf_iterator<char> input_begin(input); 
    istreambuf_iterator<char> input_end; 

    // Fill a char vector with input file's contents: 
    vector<char> char_input(input_begin, input_end); 
    input.close(); 

    // Convert it to an array of unsigned long with a cast: 
    unsigned long* converted = reinterpret_cast<unsigned long*>(&char_input[0]); 
    size_t num_long_elements = char_input.size() * sizeof(char)/sizeof(unsigned long); 

    // Put that information into a vector: 
    vector<unsigned long> long_input(converted, converted + num_long_elements); 

    return 0; 
} 

Nun, ich persönlich eher nicht mögen diese Lösung (mit reinterpret_cast, ausgesetzt char_input 's-Array), aber ich bin nicht vertraut genug mit istreambuf_iterator, um bequem eine templatized über 64-Bit-Zeichen, die th machen würde ist viel einfacher.

+1

Ich habe derzeit keine Stimmen für heute, aber wenn ich noch etwas übrig hätte, würde ich das verbessern. :-) Das ist eine ausgezeichnete Erklärung. – templatetypedef

+0

@templatetypedef mach dir keine Sorgen, ich bin nicht: P – wheaties

+1

IIRC gibt es auch Möglichkeiten, zusätzliche Klammern hinzuzufügen, um die Parsing Ambiguität zu umgehen, aber das ist hässlich und scheitert an der Explizitheit. –