// // g++ -Wall -O2 -o fnt fnt.cpp -lboost_regex-mt #include #include #include #include #include #include #include #include #include "boost/bind.hpp" #include "boost/shared_ptr.hpp" #include "boost/operators.hpp" #include "boost/regex.hpp" #include "boost/lexical_cast.hpp" using namespace std; /// Konstante für die maximale Leselänge von einem Stream namespace buffer { enum bufsiz { size = 512 }; } /// Die Klasse "file_tokenizer" liefert einen Iterator auf einen Input Stream. /// Der Input Stream wird dabei in Stücke zerteilt, die auf ein bestimmtes /// Zeichen enden oder die maximale Leselänge der Konstanten buffer::size erreichen. /// /// Ich mache mir dabei die boost Bibliothek "boost::operator" zu Nutze, um nur /// die minimale Schnittstelle implementieren zu müssen. class file_tokenizer : private boost::input_iterator_helper { /// Zugrundeliegender "input" istream * file; /// Interne Variable zum Zwischenspeichern des Stream Zustands bool ok; /// Das Trennzeichen der "tokens" unsigned char delim; /// Interner Buffer fürs Lesen vom "input" char buf[buffer::size]; /// Zur einfachen Weiterverarbeitung wird der gelesene Buffer /// als dieser string gespeichert und vom Iterator zurückgegeben string worker; public: /// Standard Konstruktor /// @param i Der zugrendliegende input stream /// @param d Das Trennzeichen der Iterator Stücke explicit file_tokenizer(istream * i = 0, unsigned char d = '\n'); /// copy constructor, dessen Implementierung boost::operator erzwingt file_tokenizer(const file_tokenizer& x); /// Vergleichsoperator. Wird hier vorallem für die das Iterator Ende /// benötigt. Implementierung boost::operator bool operator==(const file_tokenizer & x) const; /// Increase Operator, erzwungen von boost::operator file_tokenizer & operator++( void ); /// Derefernzierung zum Zugriff auf das Zugrundeliegende Element const string & operator*( void ) const; }; /// Die struktur "footnote" dient zur Speicherung aller /// Fußnoten mit der Häufigkeit der Verwendung, der Position in /// der Textdatei und der Rangfolge ihrer ersten Verwendung. struct footnote { /// Standard Konstruktor explicit footnote( unsigned int c = 0, unsigned int o = 0, unsigned int r = 0) : counter( c ), offset( o ), order( r) { } /// Häufigkeit der Verwendung unsigned int counter; /// Position in der textdatei unsigned int offset; /// Rangfolge der ersten Verwendung unsigned int order; }; /// Jede Fußnote wird in dieser Map abgelegt, indiziert durch /// ihre orginal Nummer. typedef map footnote_map; /// Der Textdatei wird beim ersten Scannen die Orginal /// Fußnotennummer und die Anzahl der Zeichen, die zwischen /// der letzten Fußnote lagen, entrissen. Diese beiden /// Zahlen werden in einer Struktur names "footnote_offset" /// gespeichert und beim zweiten Lesen der Datei verwendet. struct footnote_offset { /// Standard Konstruktor explicit footnote_offset( unsigned int id, unsigned int offset ) : id( id), offset( offset ) { } /// Orginal Nummer der Fußnote. Die Fußnote 0 hat eine /// Sonderbedeutung. Sie ist für den Anfangstext bis zur /// ersten Fußnote reserviert und soll nicht weiter- /// verarbeitet werden. unsigned int id; /// Anzahl der gelesen Zeichen seit der letzten Fußnote unsigned int offset; }; /// Das Ergebnis des ersten Textdatei Scans werden innerhalb /// einer Liste von "footnote_offset"s gespeichert. Ich /// nenne ihn "text_chunks". typedef list< footnote_offset > text_chunks; /// Nachdem wir alle Daten gesammelt haben, wird die Textdatei /// ein zweites Mal durchlaufen. Dabei werden je nach Modus die /// Fußnoten nach der Reihenfolge ihres Auftretens oder ihrer /// Häufigkeit numeriert und im Text angegeben. Um die Ausgabe /// zu erleichtern, schaffe ich mir dieses erweiterte /// Funktionsobject "print_footnote" class print_footnote { /// Die Sammelung aller gescannten Fußnoten footnote_map & fidx; /// Zugrundeliegender "input" istream * file; /// Interner Buffer fürs Lesen vom "input" char buf[buffer::size]; public: /// Standard Konstruktor /// @param fidx Die gesammelten Fußnoten /// @param file Der zugrundeliegnede "input stream" explicit print_footnote( footnote_map & fidx, istream * file ); /// Hilfsfunktion die den geöffneten stream zum Anfang zurücksetzt void rewind( void ); /// Dadurch wird dies ein Funktionsobject, das den Text ausdrucken /// kann. Intern wird der mitgegebene Textabschnitt mit der /// neuen Fußnote versehen und an "stdout" geschickt. Bei Fußnote /// "0" wird der der Verweis übersprungen. void operator()( const footnote_offset & ft ); }; /// In diesem Funktionsobjekt wird jeder String nach dem /// Fußnotenmuster gescanned und den beiden Containern "text_chunks" /// und "footnote_map" die entsprechenden Einträge hinzugefügt. class build_index { boost::regex pattern; string buffer; const string switcher; bool switched; text_chunks * text; footnote_map * footnotes; public: /// Konstruktor explicit build_index( const string & sw , text_chunks * text, footnote_map * footnotes ); /// Durchkämmt den den mitgegebenen string und erweitert /// damit die interenen Container um ... void operator()( const string & ft ); }; /// Nun ist alles vorbereitet... int main( int argc, char *argv[] ) { // Die zu bearbeitende Datei wird als erster Parameter erwartet ifstream source( argv[1] ); text_chunks text; footnote_map footnotes; build_index bldidx( "@footnotes:", &text, &footnotes ); print_footnote printit( footnotes, &source ); // Auf der Datei wird nun einen Iterator definiert, // der diese in ']' terminierte Stückchen zerteilt. file_tokenizer bos( &source,'['), eos; // Gehe mittels dieses Iteratotrs durch die Datei und baue // dabei den Index auf. for_each( bos, eos, bldidx ); // Textdatei zurücksetzten und damit bereit für den // zweiten Durchlauf sein printit.rewind(); // Jetzt kann der Textteil der Datei ausgedruckt werden for_each( text.begin(), text.end(), printit ); // Aufräumen source.close(); } ////////////////////////////////////////////////////////////////////// // Implementierungsteil ////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// file_tokenizer::file_tokenizer(istream * i, unsigned char d ) : file(i), ok(false), delim(d) { } file_tokenizer::file_tokenizer(const file_tokenizer& x) : file(x.file), ok(x.ok), delim( x.delim) { ++(*this); } bool file_tokenizer::operator==(const file_tokenizer & x) const { return ( ok == x.ok ) && ( ! ok || file == x.file ); } file_tokenizer & file_tokenizer::operator++( void ){ ok = ( file && *file && ! file->eof() ) ? true : false; if ( ok ) { worker.clear(); if ( file->peek() == delim ){ file->get(); worker.assign("["); } if ( file->peek() != delim ){ memset( buf, 0, buffer::size ); file->get( buf, buffer::size , delim ); worker.append( buf ); } } return *this; } const string & file_tokenizer::operator*( void ) const { return worker; } print_footnote::print_footnote( footnote_map & fidx, istream * file ) : fidx( fidx ), file( file ) { } void print_footnote::rewind( void ){ file->clear(); file->seekg( 0, ios::beg ); } void print_footnote::operator()( const footnote_offset & ft ){ // Nur echte Fußnoten werden auch gedruckt if ( ft.id != 0 ){ cout << '[' << fidx[ft.id].order << ']'; file->get( buf, 32, ']' ); file->get(); } memset( buf, 0, buffer::size ); file->read( buf, ft.offset ); cout << buf; } build_index::build_index( const string & sw , text_chunks * text, footnote_map * footnotes ) : pattern( "\\[([0-9]+)\\].*" ), buffer (""), switcher(sw) , switched(false), text(text), footnotes(footnotes) { } void build_index::operator()( const string & ft ){ static unsigned int footnote_counter = 0; buffer.append( ft ); size_t switch_pos = buffer.find( switcher, 0 ); if ( switch_pos != string::npos ){ switched = true; } boost::smatch footnote_id; unsigned int id = 0; unsigned int os = buffer.length(); if( 0 != boost::regex_match(buffer, footnote_id, pattern ) ){ id = boost::lexical_cast(footnote_id[1].str()); os = os - footnote_id[1].str().length() - 2; } buffer.clear(); text->push_back( footnote_offset( id, os ) ); if ( footnotes->find( id ) == footnotes->end() ) (*footnotes)[ id ] = footnote(); (*footnotes)[ id ].counter++; if ( switched ) (*footnotes)[ id ].order = footnote_counter++; }