2013-02-28 9 views
44

In letzter Zeit zu lesen, habe ich gebeten worden, eine Funktion zu schreiben, die die Binärdatei in die std::vector<BYTE> liest, wo BYTE ein unsigned char ist. Ganz schnell kam ich mit so etwas wie diesem:Wie eine binäre Datei in einen Vektor von unsigned chars

#include <fstream> 
#include <vector> 
typedef unsigned char BYTE; 

std::vector<BYTE> readFile(const char* filename) 
{ 
    // open the file: 
    std::streampos fileSize; 
    std::ifstream file(filename, std::ios::binary); 

    // get its size: 
    file.seekg(0, std::ios::end); 
    fileSize = file.tellg(); 
    file.seekg(0, std::ios::beg); 

    // read the data: 
    std::vector<BYTE> fileData(fileSize); 
    file.read((char*) &fileData[0], fileSize); 
    return fileData; 
} 

, die unnötig kompliziert und der expliziten Umwandlung zu char* zu sein scheint, dass ich gezwungen war, während die Verwendung file.read rufe mich nicht um es besser fühlen.


Eine weitere Option ist std::istreambuf_iterator zu verwenden:

std::vector<BYTE> readFile(const char* filename) 
{ 
    // open the file: 
    std::ifstream file(filename, std::ios::binary); 

    // read the data: 
    return std::vector<BYTE>((std::istreambuf_iterator<char>(file)), 
           std::istreambuf_iterator<char>()); 
} 

das ist ziemlich einfach und kurz ist, aber noch muss ich auch die std::istreambuf_iterator<char> verwenden, wenn ich in std::vector<unsigned char> bin zu lesen.


Die letzte Option, die perfekt einfach zu sein scheint, ist std::basic_ifstream<BYTE>, zu verwenden, die es irgendwie explizit zum Ausdruck, dass „Ich habe eine Eingabedatei Stream will, und ich will, es benutzen BYTE s zu lesen“:

std::vector<BYTE> readFile(const char* filename) 
{ 
    // open the file: 
    std::basic_ifstream<BYTE> file(filename, std::ios::binary); 

    // read the data: 
    return std::vector<BYTE>((std::istreambuf_iterator<BYTE>(file)), 
           std::istreambuf_iterator<BYTE>()); 
} 

aber ich bin mir nicht sicher, ob basic_ifstream eine geeignete Wahl in diesem Fall ist.

Was ist der beste Weg zum Lesen einer Binärdatei in die vector? Ich würde auch gerne wissen, was passiert "hinter der Szene" und was sind die möglichen Probleme, die ich (abgesehen von Strom nicht richtig geöffnet werden könnte, die durch einfache Überprüfung vermieden werden könnte).

Gibt es einen guten Grund, warum man hier lieber std::istreambuf_iterator verwenden möchte?
(der einzige Vorteil, dass ich sehen kann, ist die Einfachheit)

+1

@ R.MartinhoFernandes: Was ich damit meinte war, dass die dritte Option nicht besser als die zweite Option zu sein scheint. – LihO

+0

jemand hat es gemessen (in 2011), mindestens in String geladen. http://insanecoding.blogspot.hk/2011/11/how-to-read-in-file-in-c.html – jiggunjer

+0

Ein sicherer Weg, um die Größe zu finden: Verwenden Sie die spezielle ['ignore()'] (http : //en.cppreference.com/w/cpp/io/basic_istream/ignore) zähle: 'file.ignore (std :: numeric_limits :: max());', und gib die 'std :: streamsize "extrahiert" mit 'auto size =' ['file.gcount();'] (http://en.cppreference.com/w/cpp/io/basic_istream/gcount) –

Antwort

18

Wenn für die Leistungstests, würde ich einen Testfall umfasst für:

std::vector<BYTE> readFile(const char* filename) 
{ 
    // open the file: 
    std::ifstream file(filename, std::ios::binary); 

    // Stop eating new lines in binary mode!!! 
    file.unsetf(std::ios::skipws); 

    // get its size: 
    std::streampos fileSize; 

    file.seekg(0, std::ios::end); 
    fileSize = file.tellg(); 
    file.seekg(0, std::ios::beg); 

    // reserve capacity 
    std::vector<BYTE> vec; 
    vec.reserve(fileSize); 

    // read the data: 
    vec.insert(vec.begin(), 
       std::istream_iterator<BYTE>(file), 
       std::istream_iterator<BYTE>()); 

    return vec; 
} 

Mein Denken ist, daß der Konstruktor von Methode 1 die Elemente in den vector berührt, und dann die wieder read jedes Element berührt.

Methode 2 und Methode 3 sehen am vielversprechendsten aus, könnten aber unter einem oder mehreren resize leiden. Daher der Grund zu reserve vor dem Lesen oder Einfügen.

Ich würde auch mit std::copy testen:

... 
std::vector<byte> vec; 
vec.reserve(fileSize); 

std::copy(std::istream_iterator<BYTE>(file), 
      std::istream_iterator<BYTE>(), 
      std::back_inserter(vec)); 

Am Ende denke ich, dass die beste Lösung operator >> von istream_iterator vermeiden (und all den Overhead und Güte von operator >> versuchen, binären Daten zu interpretieren). Aber ich weiß nicht, was zu verwenden, dass Sie die Daten direkt in den Vektor kopieren können.

Schließlich wird meine Prüfung mit Binärdaten ios::binary wird nicht berücksichtigt. Daher der Grund für noskipws von <iomanip>.

+0

Gibt es eine Möglichkeit, eine bestimmte Größe in das Array anstelle der gesamten Datei wie hier beschrieben zu lesen? – superhero

+0

Ich dachte, du brauchst nur 'Datei.unsetf (std :: ios :: skipws); 'Wenn ich den Operator >> – jiggunjer

+0

benutze, brauche ich' file.unsetf (std :: ios :: skipws); 'auch wenn' std :: copy' verwendet wird, um nach '' zu kopieren vector', sonst hätte ich Daten verloren. Das war mit Boost 1.53.0. – phoenix

6

Da Sie die gesamte Datei in den Speicher geladen werden die optimalste Version ist die Datei in den Speicher abzubilden. Dies liegt daran, dass der Kernel die Datei sowieso in den Kernel-Seiten-Cache lädt und die Datei, die Sie gerade im Cache aufführen, Ihrem Prozess zuordnet. Wird auch als Nullkopie bezeichnet.

Wenn Sie std::vector<> verwenden, werden die Daten aus dem Kernel-Seitencache in std::vector<> kopiert, was unnötig ist, wenn Sie die Datei nur lesen möchten.

Wenn zwei Eingabe-Iteratoren an std::vector<> übergeben werden, wird auch der Puffer beim Lesen vergrößert, da die Dateigröße nicht bekannt ist. Wenn die Größe von std::vector<> zunächst auf die Dateigröße geändert wird, wird der Inhalt unnötigerweise gelöscht, da er ohnehin mit Dateidaten überschrieben wird. Beide Methoden sind hinsichtlich Raum und Zeit nicht optimal.

+0

Ja, wenn der Inhalt muss nicht in einem Vektor sein, das ist definitiv die beste Methode. –

+0

anstatt 'resize',' reserve' wird nicht initialisiert. – jiggunjer

+0

@jiggunjer Und? .. –

4

Ich hätte gedacht, dass die erste Methode, die Größe zu verwenden und stream::read() zu verwenden, am effizientesten wäre. Die "Kosten" des Castings an char * sind höchstwahrscheinlich Zero - Casts dieser Art, die dem Compiler einfach sagen: "Hey, ich weiß, dass du denkst, das ist ein anderer Typ, aber ich möchte diesen Typ hier wirklich ...", und nicht Fügen Sie zusätzliche Anweisungen hinzu - wenn Sie dies bestätigen möchten, versuchen Sie, die Datei in ein Char-Array zu lesen und vergleichen Sie den tatsächlichen Assembler-Code.Abgesehen von ein wenig zusätzlicher Arbeit, um die Adresse des Puffers innerhalb des Vektors herauszufinden, sollte es keinen Unterschied geben.

Wie immer ist die einzige Möglichkeit, in Ihrem Fall sicher zu sagen, was am effizientesten ist, sie zu messen. "Fragen im Internet" ist kein Beweis.

6
std::ifstream stream("mona-lisa.raw", std::ios::in | std::ios::binary); 
std::vector<uint8_t> contents((std::istreambuf_iterator<char>(stream)), std::istreambuf_iterator<char>()); 

for(auto i: contents) { 
    int value = i; 
    std::cout << "data: " << value << std::endl; 
} 

std::cout << "file size: " << contents.size() << std::endl; 
Verwandte Themen