In letzter Zeit wurde ich gebeten, eine Funktion zu schreiben, die die Binärdatei in das std::vector<BYTE>
wo BYTE
ist ein liest unsigned char
. Ganz schnell kam ich mit so etwas:
#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;
}
Das scheint unnötig kompliziert zu sein, und die explizite Besetzung, char*
die ich beim Telefonieren file.read
verwenden musste, lässt mich nicht besser fühlen.
Eine andere Option ist std::istreambuf_iterator
:
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, aber ich muss es trotzdem benutzen, std::istreambuf_iterator<char>
auch wenn ich hineinlese std::vector<unsigned char>
.
Die letzte Option, die vollkommen unkompliziert zu sein scheint, ist die Verwendung std::basic_ifstream<BYTE>
, die es ausdrücklich ausdrückt: "Ich möchte einen Eingabedateistream und ich möchte ihn zum Lesen von BYTE
s verwenden" :
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>());
}
Ich bin mir aber nicht sicher, ob basic_ifstream
dies in diesem Fall eine angemessene Wahl ist.
Was ist der beste Weg, um eine Binärdatei in die zu lesen vector
? Ich würde auch gerne wissen, was "hinter den Kulissen" passiert und auf welche möglichen Probleme ich stoßen könnte (abgesehen davon, dass der Stream nicht richtig geöffnet wird, was durch einfache is_open
Überprüfung vermieden werden könnte ).
Gibt es einen guten Grund, warum man es vorziehen würde, std::istreambuf_iterator
hier zu verwenden ?
(Der einzige Vorteil, den ich sehen kann, ist die Einfachheit)
quelle
ignore()
Anzahl:file.ignore(std::numeric_limits<std::streamsize>::max());
und geben Sie diestd::streamsize
'extrahierte' mitauto size =
file.gcount();
Antworten:
Beim Testen der Leistung würde ich einen Testfall hinzufügen 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; }
Meiner Meinung nach berührt der Konstruktor von Methode 1 die Elemente in
vector
und dannread
jedes Element erneut.Methode 2 und Methode 3 sehen am vielversprechendsten aus, könnten aber eine oder mehrere leiden
resize
. Daher der Grundreserve
vor dem Lesen oder Einfügen.Ich würde auch testen mit
std::copy
:... std::vector<byte> vec; vec.reserve(fileSize); std::copy(std::istream_iterator<BYTE>(file), std::istream_iterator<BYTE>(), std::back_inserter(vec));
Am Ende, ich denke , die beste Lösung vermeiden wird
operator >>
ausistream_iterator
(und alle Unkosten und Güte ausoperator >>
versuchen , binären Daten zu interpretieren). Aber ich weiß nicht, was ich verwenden soll, damit Sie die Daten direkt in den Vektor kopieren können.Schließlich zeigt mein Test mit Binärdaten,
ios::binary
dass er nicht gewürdigt wird. Daher der Grund fürnoskipws
von<iomanip>
.quelle
file.unsetf(std::ios::skipws);
wenn Sie den Operator >>file.unsetf(std::ios::skipws);
sogar beimstd::copy
Kopieren auf einvector
, sonst würde ich Daten verlieren. Dies war mit Boost 1.53.0.std::istream_iterator
verwendet den>>
Operator intern, um Daten aus dem Stream zu extrahieren.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;
quelle
Da Sie die gesamte Datei in den Speicher laden, ist es am besten, die Datei in den Speicher abzubilden. Dies liegt daran, dass der Kernel die Datei ohnehin in den Kernel-Seiten-Cache lädt und Sie durch Zuordnen der Datei nur diese Seiten im Cache in Ihren Prozess einbinden. Wird auch als Nullkopie bezeichnet.
Wenn Sie es verwenden
std::vector<>
, werden die Daten aus dem Kernel-Seiten-Cache kopiert,std::vector<>
was nicht erforderlich ist, wenn Sie nur die Datei lesen möchten.Wenn zwei Eingabe-Iteratoren übergeben werden,
std::vector<>
wird der Puffer beim Lesen vergrößert, da die Dateigröße nicht bekannt ist. Wennstd::vector<>
die Größe zuerst auf die Dateigröße geändert wird, wird der Inhalt unnötig auf Null gesetzt, da er ohnehin mit Dateidaten überschrieben wird. Beide Methoden sind räumlich und zeitlich nicht optimal.quelle
resize
,reserve
initialize tut.streambuf
undbasic
mache das?. Die Terminologie geht auch davon aus, dass Linux / UNIX das verwendete Betriebssystem ist, was möglicherweise nicht für alle Plattformen gilt. Gibt es in allen Betriebssystemen, auf die C ++ abzielt, dieselben Konzepte und Best Practices?Ich hätte gedacht, dass die erste Methode, die Größe und Verwendung
stream::read()
zu verwenden, die effizienteste wäre. Die "Kosten" für das Castingchar *
sind höchstwahrscheinlich Null - Casts dieser Art sagen dem Compiler einfach, dass "Hey, ich weiß, dass Sie denken, dass dies ein anderer Typ ist, aber ich möchte diesen Typ wirklich hier ..." und fügt nichts hinzu Zusätzliche Anweisungen - Wenn Sie dies bestätigen möchten, lesen Sie die Datei in ein char-Array 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, die Messung. "Fragen im Internet" ist kein Beweis.
quelle