Benutzer:Stefan Knauf/Kategorieeintraegesuchprogramm.cpp

aus Wikipedia, der freien Enzyklopädie
Zur Navigation springen Zur Suche springen

C++-Programm, das aus einer SQL-Datei mit den Kategorielinks (z.B. dewiki-20130906-categorylinks.sql, komprimierte Dateigröße 172,5 MB) alle Einträge aus einer bestimmten Kategorie raussucht.

Das Programm ist dazu da, um aus einer Kommandozeile aufgerufen zu werden und beim Aufruf drei Parameter übergeben zu bekommen: Als ersten den Namen der SQL-Datei, als zweiten den Namen der Kategorie, wobei Leerzeichen durch Unterstriche zu ersetzen sind, und als dritten den Namen, den die Ausgabedatei erhalten soll. Ein korrekter Programmaufruf sieht beispielsweise so aus:

Kategorieeintraegesuchprogramm dewiki-20130906-categorylinks.sql Beispielkategorie Ausgabe.txt

In besagter SQL-Datei stehen die Kategorielinks in folgender Form:

(irgendeine Nummer,'Name_der_Kategorie','LEMMA',irgendwelche anderen Daten)

Wenn das Lemma vom Sortierschlüssel in der Kategorie abweicht, sieht die Form so aus:

(irgendeine Nummer,'Name_der_Kategorie','SORTIERSCHLÜSSEL\nLEMMA',irgendwelche anderen Daten)

Lemma und Sortierschlüssel sind dabei kapitalisiert (d.h. in Großbuchstaben), im Namen der Kategorie sind Leerzeichen durch Unterstriche ersetzt. Im Beispiel der Eintragung des Artikels Sankt Augustin in die Kategorie Gemeinde in Nordrhein-Westfalen sieht das so aus:

(29136,'Gemeinde_in_Nordrhein-Westfalen','SANKT AUGUSTIN','2007-10-30 13:37:56','','uppercase','page')

Das Programm durchsucht folglich die SQL-Datei nach allen Vorkommen des Musters (irgendeine Nummer,'Name_der_Kategorie','. Ist so eine Zeichenfolge gefunden, muss das Programm prüfen, ob Sortierschlüssel und Lemma voneinander abweichen. Dies tut es, indem es prüft, ab nach dieser Zeichenfolge das Zeichen ' oder die Zeichenfolge \n früher auftaucht. Im ersten Fall liest das Programm einfach alles Kommende bis zu diesem ' als Lemma ein, im letzteren Fall alles ab der Zeichenfolge \n bis zum nächsten '. Anschließend schreibt das Programm alle gefundenen Lemmata in die Ausgabedatei. Die Ausgabedatei ist wieder in UTF-8 kodiert, wie die SQL-Datei auch. Die Lemmata stehen in der Ausgabedatei natürlich auch in kapitalisierter Form.



//C++

// 25.9.2013


using namespace std;

#include<iostream> //enthält die Befehle zum Schreiben auf den Bildschirm
#include<fstream> //enthält die Befehle zum Schreiben in und zum Lesen aus Dateien
#include<string> //enthält den Kram zu Strings, also zu Zeichenketten
#include<vector> //für die praktische Vektor-Klasse vector<irgendein Typ>
#include<time.h>  //enthält time(NULL)


void SchreibeVectorinDatei (const vector<string> & Liste, const string & Ausgabedateiname) //schreibt alle Strings des vectors in die Ausgabedatei, wobei jeder String seine eigene Zeile erhält und vor den ersten String die Zeichenfolge "" gesetzt wird, um anzuzeigen, dass die Datei in UTF-8 kodiert sein wird
//Sinnvollerweise müssen sich die Bytes der Strings zu UTF-8 zusammenfügen, sonst macht diese Prozedur Unsinn.
 {ofstream Datei;
  Datei.open(Ausgabedateiname.c_str());
  Datei << static_cast<char>(-17) << static_cast<char>(-69) << static_cast<char>(-65); //um anzuzeigen, dass die Ausgabedatei in UTF-8 kodiert ist
  for (vector<string>::size_type i; i<Liste.size(); i++)
   {Datei << Liste[i] << '\n';
   }
  Datei.close();
 }

bool IstZiffer (const char & Zeichen)
 {return ((Zeichen>='0')&&(Zeichen<='9'));
 }

vector<string> Zeilenliste (const string & Zeile, const string & Kategorie)
 {vector<string> Teilliste(0);
  string::size_type n=0;
  while (n!=string::npos)
   {n = Zeile.find(",'"+Kategorie+"','", n);
    if (n<Zeile.size())
     {if(n>1)
       {bool passtschon=false;
        if (IstZiffer(Zeile[n-1])) //ab hier wird geprüft, ob das folgende Muster vorliegt: ([Ziffern],'[Kategorie]','
         {string::size_type i=2;
	      while ((i+1<=n)&&IstZiffer(Zeile[n-i])) //Vorsicht, in dieser Schleife wird rückwärts durch den String gegangen
           {i++;
           }
          if (Zeile[n-i]=='(')
           {passtschon=true;
           }
         }
        if (passtschon) //hier ist auf einen Kategorieeintrag gestoßen worden, dieser muss korrekt identifiziert werden
         {string::size_type j = Zeile.find("'", n+5+Kategorie.size());
          if (j!=string::npos)
           {string::size_type k = Zeile.find("\\n", n+5+Kategorie.size());
            string Lemma;
            if (k<j) //hier weichen Sortierschlüssel und Lemma voneinander ab
             {Lemma.resize(j - (k+2));
              for (string::size_type l = 0; l<Lemma.size(); l++)
               {Lemma[l]=Zeile[k+2+l];
               }
             }
            else //hier ist der Artikel mit seinem Lemma in die Kategorie einsortiert
             {Lemma.resize(j - (n+5+Kategorie.size()));
              for (string::size_type l = 0; l<Lemma.size(); l++)
               {Lemma[l]=Zeile[n+5+Kategorie.size()+l];
               }
             }
            Teilliste.push_back(Lemma);
           }
          else
           {cerr << "Hier scheint das Lemma zu fehlen! Zumindest fehlt ein abschließendes ', das normalerweise das Ende des Lemmas kennzeichnet." << endl;
           }
         }
       }
      n++; 
     }
   }
  return Teilliste;
 }

vector<string> Eintragsliste (const string & Eingabedateiname, const string & Kategorie)
 {ifstream Datei;
  vector<string> Liste(0);
  Datei.open(Eingabedateiname.c_str());
  if (!Datei.good())
   {cerr << "Beim Öffnen der Datei " << Eingabedateiname << " ist ein Fehler aufgetreten!" << endl;
   }
  while (Datei.good())
   {string Zeile;
    getline(Datei, Zeile);
    vector<string> Teilliste=Zeilenliste(Zeile, Kategorie);
    Liste.insert(Liste.end(), Teilliste.begin(), Teilliste.end());
   }
  Datei.close();
  return Liste;
 }
 
int main(int argc, char ** argv)
 {time_t Startzeit = time(NULL);
  tm* Zeitzeiger = localtime(&Startzeit);
  int Minuten =(*Zeitzeiger).tm_min;
  cout << "Das Programm wurde um " << (*Zeitzeiger).tm_hour << ":";
  Zeitzeiger=NULL;
  if (Minuten<10) //sorgt dafür, dass die Minuten stets zweistellig angegeben werden
   {cout << 0;
   }
  cout << Minuten << " Uhr gestartet." << endl;
  if (argc>3)
   {string Eingabedateiname=argv[1];
    string Kategorie=argv[2];
    string Ausgabedateiname=argv[3];
    vector<string> DieListe=Eintragsliste(Eingabedateiname, Kategorie);
    SchreibeVectorinDatei(DieListe, Ausgabedateiname);
    cout << '\n';
    if (DieListe.size()==1)
     {cout << "Es wurde genau ein Eintrag der Kategorie \"" << Kategorie << "\" gefunden.\n";
     }
    else
     {cout << "Es wurden " << DieListe.size() << " Einträge der Kategorie \"" << Kategorie << "\" gefunden.\n";
     }
    cout << "Die Ausgabe befindet sich in der Datei " << Ausgabedateiname << ".\n";
   }
  else //hier wurden dem Programm zu wenige Parameter übergeben
   {cout << "Dem Programm müssen bei Aufruf drei Parameter übergeben werden.\n";
    cout << "Als ersten den Namen der SQL-Datei mit den Kategorielinks und als zweiten den Namen der Kategorie, deren Einträge aufgelistet werden sollen, und als dritten den Namen, den die Ausgabedatei erhalten soll.\n";
    cout << "Ein korrekter Programmaufruf sieht beispielsweise so aus:\n";
    cout << argv[0] << " dewiki-20130906-categorylinks.sql Beispielkategorie Ausgabe.txt\n";
   }
  cout << '\n';
  time_t Laufzeit=time(NULL) - Startzeit;
  if (Laufzeit==1)
   {cout << "Das Programm hat für seinen Lauf eine Sekunde benötigt.\n";
   }
  else
   {cout << "Das Programm hat für seinen Lauf " << Laufzeit << " Sekunden benötigt.\n";
   }
  cout << '\a' << endl; //gibt einen Ton aus, um das Ende des Programms zu signalisieren
  return 0;
 }