istream_iterator mit eigenem Elementtyp
-
Hallo zusammen!
Mich beschäftigt gerade das Lokalisieren von bestimmten Datenabschnitten in Binärdateien. Ein solcher Abschnitt kann prinzipiell überall in der Datei stehen und wird durch ein Marker-Wort eingeleitet. Die Länge dieser Markierungen ist dabei konstant - setzen wir sie der Einfachkeit halber mal auf vier fest.
Ich suche nun nach einer möglichst eleganten und generischen Lösung. std::find() mit einem istream_iterator sieht nach einem möglichen Lösungansatz aus, allerdings stellt sich die Frage nach dem richtigen Elementtyp. Mir kam eine Struktur, die aus 4 Zeichen besteht, in den Sinn:
struct Word4 { char a, b, c, d; }Allerdings scheinen dafür noch Operatoren überladen werden zu müssen, denn beim Erstellen erscheint folgender Fehler:
cd '/home/martin/Entwicklung/C++/Wave/WaveTest/debug/./src' && WANT_AUTOCONF_2_5="1" WANT_AUTOMAKE_1_6="1" LC_MESSAGES="C" LC_CTYPE="C" gmake -k wavetest
g++ -DHAVE_CONFIG_H -I. -I/home/martin/Entwicklung/C++/Wave/WaveTest/src -I.. -O0 -g3 -MT wavefile.o -MD -MP -MF .deps/wavefile.Tpo -c -o wavefile.o /home/martin/Entwicklung/C++/Wave/WaveTest/src/wavefile.cpp
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stream_iterator.h: In member function 'void std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::_M_read() [with _Tp = Word4, _CharT = char, _Traits = std::char_traits<char>, _Dist = int]':
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stream_iterator.h:68: instantiated from 'std::istream_iterator<_Tp, _CharT, _Traits, _Dist>::istream_iterator(std::basic_istream<_CharT, _Traits>&) [with _Tp = Word4, _CharT = char, _Traits = std::char_traits<char>, _Dist = int]'
/home/martin/Entwicklung/C++/Wave/WaveTest/src/wavefile.cpp:52: instantiated from here
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/bits/stream_iterator.h:119: error: no match for 'operator>>' in '((std::istream_iterator<Word4, char, std::char_traits<char>, int>)this)->std::istream_iterator<Word4, char, std::char_traits<char>, int>::_M_stream >> ((std::istream_iterator<Word4, char, std::char_traits<char>, int>)this)->std::istream_iterator<Word4, char, std::char_traits<char>, int>::_M_value'
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:123: note: candidates are: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_istream<_CharT, _Traits>& ()(std::basic_istream<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:127: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_ios<_CharT, _Traits>& ()(std::basic_ios<_CharT, _Traits>&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:134: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::ios_base& ()(std::ios_base&)) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:170: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(bool&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:174: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(short int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:177: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(short unsigned int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:181: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:184: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(unsigned int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:188: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(long int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:192: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(long unsigned int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:197: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(long long int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:201: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(long long unsigned int&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:206: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(float&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:210: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(double&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:214: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(long double&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:218: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(void*&) [with _CharT = char, _Traits = std::char_traits<char>]
/usr/lib/gcc/i386-redhat-linux/4.3.0/../../../../include/c++/4.3.0/istream:242: note: std::basic_istream<_CharT, _Traits>& std::basic_istream<_CharT, _Traits>::operator>>(std::basic_streambuf<_CharT, _Traits>*) [with _CharT = char, _Traits = std::char_traits<char>]
gmake: *** [wavefile.o] Error 1
gmake: Target `wavetest' not remade because of errors.
*** Exited with status: 2 ***Leider reichen meine Kenntnisse nicht soweit, um aus der Fehlermeldung schlau zu werden. Offensichtlich muss ich irgendeinen >>-Operator überladen, abeer welchen genau und was muss dieser machen?
Kamm mir jemand von der Community helfen?
Vielen Dank im Voraus!
-
Nichts für ungut, aber die Art wie du deine Frage stellst, deutet darauf hin, dass dir das nötige Hintergrundwissen fehlt:
http://www.c-plusplus.net/forum/viewtopic-var-t-is-232010.html
Wenn du das verstanden hast, wirst du auch dein Problem lösen können, ansonsten frag nochmal. Und das nächste mal bitte mit etwas Programmcode, denn aus einer solchen Fehlermeldung alleine wird man nicht schlau genug um eine präzise Antwort geben zu können.
-
Hallo SeppJ,
danke für die Richtungsweisung - mir ist klar geworden, dass ich selbst festlegen muss, wie ein Objekt aus einem Stream geladen wird (Überladung des >>-Operators).
Mein Code sieht also wie folgt aus:
#include <fstream> #include <iterator> #include <iostream> #include <algorithm> struct Word4 { char a, b, c, d; Word4() {}; Word4(const std::string& str) { this->a = str[0]; this->b = str[1]; this->c = str[2]; this->d = str[3]; } }; std::istream& operator>>(std::istream& lhs, Word4& rhs) { lhs.read(reinterpret_cast<char*>(&rhs), sizeof(rhs)); lhs.seekg(-3, std::ios::cur); return lhs: } bool operator==(const Word4& lhs, const Word4& rhs) { return lhs.a == rhs.a && lhs.b == rhs.b && lhs.c == rhs.c && lhs.d == rhs.d; } int main(int argc, char* argv[]) { std::ifstream wavefile(argv[1].c_str(), std::ifstream::binary); std::istream_iterator<Word4> iterStart(wavefile), iterEnd, iterFound; iterFound = std::find(iterStart, iterEnd, std::string("data")); if (iterFound == iterEnd) { std::cout << "NOT FOUND" << std::endl; } else { std::cout << "FOUND AT POSITION " << std::distance(iterStart, iterFound) << std::endl; }; }Die Ausgabe, ob das Element gefunden wurde oder nicht funktioniert nun korrekt. Allerdings bringt es mir nichts, allein die Existenz festzustellen - ich benötige die Position. Leider gibt mir std::distance immer 0 zurück, warum ist das so? Laut Dokumentation sollte das doch die Distanz zwischen zwei Iteratoren angeben, in meinem Fall also der Abstand vom Dateianfang.
-
Chewie83 schrieb:
Die Ausgabe, ob das Element gefunden wurde oder nicht funktioniert nun korrekt. Allerdings bringt es mir nichts, allein die Existenz festzustellen - ich benötige die Position. Leider gibt mir std::distance immer 0 zurück, warum ist das so? Laut Dokumentation sollte das doch die Distanz zwischen zwei Iteratoren angeben, in meinem Fall also der Abstand vom Dateianfang.
iterEnd wird nirgendwo gesetzt.
-
SeppJ schrieb:
iterEnd wird nirgendwo gesetzt.
Daran liegt es nicht, vor allem wurde
iterEndkorrekt angelegt.@Chewie83,
1. Dein Code hat Abtippfehler drin. Wieso verwendest du kein Copy&Paste?
2. Der Hauptfehler besteht darin, dass duistream_iteratorObjekte vergleichst. Das passiert nämlich instd::distance, weilistream_iteratorkein Random Access Iterator ist. Der erste übergebene Iterator wird solange erhöht, bis er gleich dem zweiten ist. Nebenher wird ein Zähler mitgeführt. Nun, zweiistream_iteratorObjekte sind per Standard dann gleich, wenn sie auf den gleichen Stream verweisen. Also sind sie sofort gleich und die Distanz ist 0.
3. Auch wenn dein Vorhaben gelingen würde, dann hast du den Fehler gemacht, dass einistream_iteratorintern nur einen Zeiger auf den Stream hält. Also müsstest du zuerst den Stream wieder auf die Startposition setzen und alle Lesevorgänge nochmals ausführen, weil eben über die Erhöhung des Iterators, wie in Punkt 2 beschrieben, die Distanz ermittelt wird.
4. Wie du es dir schon fast denken kannst, für sowas ist deristream_iteratornicht gedacht. Mit demistream_iteratorsoll man einmal drüber gehen und dann die Iteratoren wegschmeissen. Sie sind wie Einweg-Pettflaschen
Als Lösung bietet es sich an, dass man entweder hier keinen
istream_iteratorbenutzt und es auf "normale" Wege löst oder eine eigeneistream_iteratorKlasse erfindet, welche vor dem auslesen jeweils die Position speichert. Wäre ziemlich einfach zu realisieren.
Hier findest du eine Implementations eineristream_iteratorKlasse:
http://www.cplusplus.com/reference/std/iterator/istream_iterator/Die kann man leicht selber erweitern.
Grüssli
-
Hallo Dravere,
danke für Deine Antwort. Mit viel Mühe habe ich ausgehend vom verlinkten Code eine Klasse
istream_pos_iteratorerstellt. Sie sieht wie folgt aus:namespace std { template <class T, class charT=char, class traits=char_traits<charT>, class Distance=ptrdiff_t> class istream_pos_iterator : public iterator<input_iterator_tag, T, Distance, const T*, const T&> { basic_istream<charT,traits>* in_stream; T value; int position; public: typedef charT char_type; typedef traits traits_type; typedef basic_istream<charT,traits> istream_type; istream_pos_iterator() : in_stream ( 0 ) {} istream_pos_iterator ( istream_type& s ) : in_stream ( &s ), position(-1) { ++*this; } istream_pos_iterator ( const istream_pos_iterator<T,charT,traits,Distance>& x ) : in_stream ( x.in_stream ), value ( x.value ), position(x.position) {} ~istream_pos_iterator() {} const T& operator*() const { return value; } const T* operator->() const { return &value; } istream_pos_iterator<T,charT,traits,Distance>& operator++() { position = in_stream->tellg(); if ( in_stream && ! ( *in_stream >> value ) ) in_stream=0; return *this; } istream_pos_iterator<T,charT,traits,Distance> operator++ ( int ) { istream_pos_iterator<T,charT,traits,Distance> tmp = *this; ++*this; return tmp; } int tell() { return position; } bool equal(const istream_pos_iterator<T,charT,traits,Distance>& o) const { if (!this->in_stream) { return (!o.in_stream); // EQUAL if both are EOF iterators, otherwise NOT EQUAL } return this->position == o.position; // Otherwise compare position } }; template <class T, class charT, class traits, class Distance> bool operator==(const istream_pos_iterator<T, charT, traits, Distance>& lhs, const istream_pos_iterator<T, charT, traits, Distance>& rhs) { return lhs.equal(rhs); } template <class T, class charT, class traits, class Distance> bool operator!=(const istream_pos_iterator<T, charT, traits, Distance>& lhs, const istream_pos_iterator<T, charT, traits, Distance>& rhs) { return !lhs.equal(rhs); } }Kann man das so stehen lassen? Was mir nicht gefällt ist die Funktion
equal, aber ich hab es partout nicht geschafft, den Vergleichsoperator alsfriendzu deklarieren. Wie sähe die Deklaration in diesem Fall aus?