/// 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<file_tokenizer, string> {
/// 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<unsigned int, footnote> 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();
}
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<unsigned int>(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++;