2009-11-29 10 views
12

Gibt es trotzdem eine bekannte Anzahl von Bytes direkt in eine std :: string zu lesen, ohne dafür einen temporären Puffer zu erstellen?Direkt von einem std :: istream in einen std :: string lesen

zB zur Zeit kann ich es tun, indem

boost::uint16_t len; 
is.read((char*)&len, 2); 
char *tmpStr = new char[len]; 
is.read(tmpStr, len); 
std::string str(tmpStr, len); 
delete[] tmpStr; 
+0

Sie gedacht haben, einen 'Vektor ' statt 'string' über die Verwendung? Wenn Ihre Daten "roher" als "string-like" sind, kann es für Sie besser funktionieren, und es gibt weniger Verwirrung über direkten Zugriff. (Vektoren müssen zusammenhängend gespeichert werden, verwenden Sie also & v [0] '.) –

+0

In den meisten Fällen handelt es sich um String-Daten, die nur in großen Binärdateien eingebettet sind.Außerdem möchte ich nur die Laderoutinen ändern, nicht die 1000er Codezeilen, die dann die einmal geladenen Daten verwenden, was eine Änderung von std :: string erfordern würde. –

+0

Dann würde ich Ihre spezifische String-Implementierung überprüfen und dann GMans Antwort verwenden, stellen Sie sicher, dass Sie den Stream auch nach 'is.read' überprüfen. –

Antwort

11

std::string hat eine resize Funktion, die Sie könnten, oder einen Konstruktor verwenden, die das gleiche tun werden:

boost::uint16_t len; 
is.read((char*)&len, 2); 

std::string str(len, '\0'); 
is.read(&str[0], len); 

Dies ist nicht getestet, und ich Ich weiß nicht, ob Strings einen zusammenhängenden Speicher benötigen.

+0

Strings sind als Vektoren definiert. Gleiche Kontiguität. – bmargulies

+4

Sie sind nicht als Vektoren definiert, 21.3.4/1 impliziert jedoch eine zusammenhängende Speicherung. Es gibt jedoch Verwirrung und Fehlerberichte über diesen speziellen Abschnitt, und ich bin mir nicht sicher, was der aktuelle Konsens ist, noch wie tragbar diese Interpretation ist. –

+2

@Roger. Ich stimme nicht zu, dass 21.3.4/1 zusammenhängende Speicherung bedeutet. Es ist das Vorhandensein von c_str() und data(), die es implizieren, aber nur, weil eine effiziente Implementierung einen zusammenhängenden Speicher erfordern würde, um sie zu implementieren. Ich glaube, dass die nächste Version des Standards auch die Situation deutlich macht. –

0

Sie optimieren nur die Codelänge oder versuchen, sich hier eine Kopie zu ersparen? Was ist falsch mit dem temporären Puffer?

Ich würde argumentieren, dass Sie tatsächlich den Schutz der Schnur umgehen versuchen, direkt zu versuchen, es so zu tun. Wenn Sie sich Sorgen über die Leistung der Kopie in einer std :: string machen, weil Sie festgestellt haben, dass sie die Leistung Ihrer Anwendung in irgendeiner Weise beeinflusst, würde ich direkt mit dem char * arbeiten.

EDIT:. Doing mehr suchen ... initializing std::string from char* without copy

In der zweiten Antwort, es ist ziemlich flach festgestellt, dass Sie nicht erreichen können, was Sie suchen (dh zu erreichen, füllen Sie eine std :: string ohne eine Wiederholung über den char * zu kopieren.)

Werfen Sie einen Blick auf Ihre Load-Routine (Post es hier vielleicht?) und minimieren Zuweisungen: neu und löschen sind sicherlich nicht frei, so dass Sie zumindest etwas Zeit sparen können, wenn Sie müssen den Puffer nicht ständig neu erstellen. Ich finde es immer hilfreich, es zu löschen, indem ich den Puffer auf 0 oder null setze, um den ersten Index des Arrays bei jeder Iteration zu beenden, aber Sie können diesen Code im Interesse der Leistung schnell entfernen, sobald Sie in Ihrem Algorithmus sicher sind.

+0

Die Leistung von std :: string ist gut, das Problem ist das Laden der Daten in sie aus einer Binärdatei, die derzeit eine unannehmbar lange Zeit in Anspruch nimmt. Profiling zeigte, dass 70% dieser Ladezeit Strings lesen, wobei nur 30% andere binäre Daten oder kleine Bits der Verarbeitung sind. Eine Beschleunigung des String-Lesens scheint daher die naheliegende Lösung zu sein, um das Ganze mit einem großen Vorsprung zu beschleunigen. Also will ich keinesfalls std :: string im Rest des Programms ersetzen, was bedeuten würde, 1000 Zeilen zu ändern, anstatt nur die String-Lade-Routine zu ändern. –

+0

Wie groß ist das Problem alloc, dealloc des char * bei jeder Iteration? Was wäre, wenn Sie einfach ein char * von ausreichender Größe (überprüfen Sie für jede Iteration, offensichtlich) herum und nur neue Zeichenfolgen aus diesem einzelnen char *? – antik

2

Man könnte so etwas wie getline verwenden:

#include <iostream> 
#include <string> 
using namespace std; 

int main() { 
    string str; 
    getline (cin,str,' '); 
} 
+1

Dies ist ein guter Vorschlag für andere Probleme, aber nicht für diesen: unformatierte Eingabe einer bestimmten Anzahl von Bytes. –

+0

Dies beantwortet die Frage nicht, da eine bestimmte Anzahl von Bytes nicht gelesen wird. Selbst wenn dies der Fall ist, muss getline jedes gelesene Byte nach dem Trennzeichen durchsuchen, was teuer und unnötig ist, wenn die Anzahl der Bytes spezifiziert ist. Diese Antwort sollte entfernt werden. – xaxxon

2

Ich würde einen Vektor als Puffer verwenden.

boost::uint16_t len; 
is.read((char*)&len, 2); // Note if this file was saved from a different architecture 
         // then endianness of these two bytes may be reversed. 

std::vector buffer(len); // uninitialized. 
is.read(&buffer[0], len); 

std::string str(buffer.begin(),buffer.end()); 

Obwohl Sie wahrscheinlich mit der Verwendung einer Zeichenfolge als Puffer (wie von GMan beschrieben). Es ist nicht durch den Standard garantiert, dass ein Strings-Member an aufeinanderfolgenden Orten ist (also überprüfe deine aktuelle Implementierung und schreibe einen großen Kommentar, den sie überprüfen muss, wenn sie auf einen anderen Compiler/eine andere Plattform portiert).

+0

"Es ist nicht durch den Standard garantiert, dass ein Strings Mitglieder in aufeinanderfolgenden Standorten sind" <== Es ist offenbar seit '11 – xaxxon

+0

@xaxxon: True. Der obige Code erfordert jedoch keine Zeichenfolge zum Speichern von Elementen an aufeinanderfolgenden Orten. Wenn Sie nun auf Vektor Bezug nehmen (und die Zeichenfolge versehentlich erwähnt haben), macht dieser Code diese Annahme. Aber wie Sie seit C++ 11 festgestellt haben, ist dies garantiert. Auch bevor der C++ - Standard 2011 aktualisiert wurde, gab es eine Übersicht über alle wichtigen Implementierungen (um 2007) und alle implementierten Vektoren als zusammenhängende Blöcke (was die Aktualisierung des Standards leicht machte). –

5

Sie könnten eine Kombination aus copy_n verwenden und eine insert_iterator

void test_1816319() 
{ 
    static char const* fname = "test_1816319.bin"; 
    std::ofstream ofs(fname, std::ios::binary); 
    ofs.write("\x2\x0", 2); 
    ofs.write("ab", 2); 
    ofs.close(); 

    std::ifstream ifs(fname, std::ios::binary); 
    std::string s; 
    size_t n = 0; 
    ifs.read((char*)&n, 2); 
    std::istream_iterator<char> isi(ifs), isiend; 
    std::copy_n(isi, n, std::insert_iterator<std::string>(s, s.begin())); 
    ifs.close(); 
    _unlink(fname); 

    std::cout << s << std::endl; 
} 

kein Kopieren, keine Hacks, keine Möglichkeit, überrannt, kein undefiniertes Verhalten.

+0

Wenn Sie tun, was ich denke, tun Sie dann lesen Sie diese [Link] (http://www.boost.org/doc/libs/1_46_0/libs/serialization/doc/index.html) und den Code das gehört dazu. –

+0

Nicht der Fall hier, aber ist die 'copy_n' sicher, wenn das Ende der Datei oder ein Fehler aufgetreten ist? – Liviu

+0

Ich habe mit Ihrer Methode einen Code erstellt: [code review] (http://codereview.stackexchange.com/questions/38148/updating-a-file-through-c-streams). Vielen Dank! – Liviu

0

Eine einfache Möglichkeit wäre:

std::istream& data 
const size_t dataSize(static_cast<size_t>(data.rdbuf()->in_avail())); 
std::string content; 
content.reserve(dataSize); 
data.read(&content[0], dataSize);