C++-Programm, das eine UTF-8-kodierte Datei nach einer Liste von Wörtern durchsucht. Geschrieben ist es dafür, um nachzuzählen, wie oft welcher Vorname in einem XML-Dump der deutschen Wikipedia vorkommt. Das Rechenergebnis bezüglich der Vornamenshäufigkeit in einem XML-Dump vom 6.9.2013 ist dort.

Das Programm ermittelt zu jedem Suchwort die Anzahl der Funde auf acht verschiedene Weisen:

Bei der ersten wird jeder Fund der Zeichenfolge gezählt, beispielsweise würde das Suchwort „Anne“ im Wort „Annexion“ gefunden.
Bei der dritten Zählweise werden Funde der Zeichenfolge nur gezählt, wenn dahinter kein Buchstabe folgt. Welches Zeichen als Buchstabe gewertet wird, entscheidet die Funktion Buchstabe aus dem unten angegebenen Programm. Das sind alle lateinischen Buchstaben mit und ohne diakritische Zeichen. Beispielsweise würde das Suchwort „Stefan“ dabei im Wikipedia-Benutzernamen „Stefan64“ gefunden, weil die Ziffer 6 nicht als Buchstabe gezählt wird. „Anne“ würde in „Annexion“ aber nicht gefunden, weil das x ein Buchstabe ist.
Bei der fünften Zählweise werden Funde der Zeichenfolge nur gezählt, wenn dahinter ein explizit als wortbeendend gewertetes Zeichen gefunden wird. Dabei werden sehr viele Zeichen als wortbeendend gewertet, genauer alle Zeichen, die von einer der beiden Funktionen wortbeendendesZeichenrestriktiv und wortbeendendesZeichen erkannt werden. Das sind Satzzeichen, Leerzeichen und einige Sonderzeichen und Prozentkodierungen bestimmter Satzzeichen. Ziffern oder Buchstaben gehören jedoch nicht dazu.
Bei der siebten Zählweise werden weniger Zeichen als wortbeendend gewertet, nämlich nur noch die Zeichen, die von der Funktion wortbeendendesZeichenrestriktiv erkannt werden. Das sind nur noch Satzzeichen und Leerzeichen. Da in Unicode eine Menge verschiedene Leer- oder Satzzeichen vorkommen, sind das immer noch einige Zeichen.
Bei der achten Zählweise werden Vorkommen eines Suchwortes nur gezählt, wenn sie von einem Leerzeichen gefolgt werden (von dem einen wahren ASCII-Leerzeichen). Das hat den Grund, dass bei den anderen Zählweisen „Jan“ der am dritthäufigsten gezählte Vorname ist, weil „Jan.“ die Abkürzung für Januar ist. Die beiden am häufigsten gezählten Vornamen sind August und April, was wohl daran liegt, das es auch Monatsnamen sind.
Bei der zweiten, vierten und sechsten Zählweise wird etwa wie bei der dritten, fünften und siebten gezählt. Allerdings werden hierbei auch Funde berücksichtigt, bei denen das Suchwort von einem Genitiv-s gefolgt wird. Das Programm weiß aber, dass Wörter, die schon auf s, x oder z enden, kein Genitiv-s erhalten können. Diese Wörter werden bei der zweiten, vierten und sechsten Zählweise genau wie bei der dritten, fünften und siebten gezählt. Die anderen Suchwörter werden auch gezählt, wenn sie von einem Genitiv-s gefolgt werden, solange danach ein für die jeweilige Zählweise wortbeendendes Zeichen folgt. Beispielsweise würde dabei der Name „Vera“ in „Veras Fahrrad“ gefunden, in „Verantwortung“ oder „Veraschung“ aber nicht.

Das Programm ist dafür da, aus einer Kommandozeile aufgerufen zu werden. Ihm müssen beim Aufruf drei Parameter übergeben werden. Als ersten den Namen der Datei mit der Liste der Suchwörter, als zweiten den Namen der zu durchsuchenden Datei und als dritten den Namen, den die Ausgabedatei erhalten soll. Ein korrekter Programmaufruf sieht beispielsweise so aus:

Namenzaehlprogramm Namensliste.txt dewiki-20130906-pages-articles-multistream.xml Ausgabe.txt

Die Dateinamen dürfen natürlich keine Leerzeichen enthalten. In der Datei mit der Liste der Suchwörter muss in jeder Zeile genau ein Suchwort und sonst nichts stehen. Sowohl die Liste der Suchwörter als auch die zu durchsuchende Datei müssen in UTF-8 kodiert sein. Die Ausgabedatei wird ebenfalls in UTF-8 kodiert.

In der Ausgabedatei bekommt wieder jedes Suchwort eine eigene Zeile. Jede Zeile beginnt mit dem zugehörigen Suchwort, dann zwei Tabulatorzeichen und dann folgen die Zahlen zu jeder der acht Zählweisen, wobei die Zahlen wieder durch Tabulatorzeichen getrennt sind. Dieses Ausgabeformat ist dazu bestimmt, dass man die Ausgabe gut in ein Tabellenkalkulationsprogramm (ich nutze LibreOffice Calc) kopieren kann. Dabei kommt dann nämlich jeder Eintrag in eine eigene Tabellenzelle, wobei zwischen den Suchwörtern und den Zahlen noch eine Spalte frei bleibt. Diese Spalte diente bei der Vornamenzählung dazu, das Geschlecht zum Vornamen zu vermerken.

Mein C++-System (Dev-C++ 5.4.2 mit dem GNU-Compiler TDM-GCC 4.7.1 64-bit Release) interpretiert den Datentyp char als signed char. Ich habe nicht ausgetestet, ob das Programm immer noch richtig rechnet, wenn char als unsigned char interpretiert werden. Ob das Programm richtig rechnet, kann einfach damit getestet werden, ob es mit der Testnamensliste und dem Testtext die richtige Ausgabe berechnet.



//C++

// 26.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)


bool Buchstabe(const string & Zeile, const string::size_type & Position) //Prüft, ob im String an der angegebenen Position eine (möglicherweise ein Byte kurze) UTF-8-Kette beginnt, die für einen (lateinischen) Buchstaben steht
//Die übergebene Position muss im String ansprechbar sein, sonst gibt's Speicherbereichsverletzungen. Dieser Fall muss vor Aufruf ausgeschlossen sein.
 {if (((Zeile[Position]>='a')&&(Zeile[Position]<='z'))||((Zeile[Position]>='A')&&(Zeile[Position]<='Z')))
   {return true;
   }
  if ((Zeile[Position] <= static_cast<char>(-56))&&(Zeile[Position] >= static_cast<char>(-61))) //ist erfüllt, wenn das Zeichen ein diakritisches Zeichen oder ÷ oder × ist, deckt alle halbwegs normalen diakritischen Zeichen ab
   {if (Zeile[Position]==static_cast<char>(-61)) //C3
     {if (Zeile.size()<=Position+1)
       {cerr << "Das Byte C3 taucht am Zeilenende auf und verstößt damit gegen die UTF-8-Kodierung!" << endl;
       }
      else
       {return (!((Zeile[Position + 1]==static_cast<char>(-73))||(Zeile[Position + 1]==static_cast<char>(-105)))); //hier wird der Zeichen ÷ und × gedacht
       }
     }
    else
     {return true;
     }
   }
  else
   {if (Zeile[Position]==static_cast<char>(-31)) //E1
     {if (Zeile.size()<=Position+2)
       {cerr << "Das Byte E1 taucht so weit am Zeilenende auf, dass keine zwei Bytes mehr dahinter stehen, und verstößt damit gegen die UTF-8-Kodierung!" << endl;
       }
      else
       {return ((Zeile[Position+1]>=static_cast<char>(-72))&&(Zeile[Position+1]<=static_cast<char>(-69))); //deckt ein paar exotische diakritische Zeichen ab (z.B. ein h mit Punkt drunter), ein "lateinisches Delta" und ein großes ß
       }
     }
   }
  return false;
 }

bool weitereswortbeendendesZeichen (const string & Zeile, const string::size_type & Position) //prüft, ob im String an der übergebenen Position eine UTF-8-Kette oder eine Prozentkodierung kommt, die für ein explizit als wortbeendendes Zeichen bekanntes Zeichen steht
//Prüft ausschließlich auf ein paar weitere Zeichen, die nicht in der untenstehenden Funktion wortbeendendesZeichenrestriktiv abgefragt werden.
//Die übergebene Position muss im String ansprechbar sein, sonst gibt's Speicherbereichsverletzungen. Dieser Fall muss vor Aufruf ausgeschlossen sein.
 {switch (Zeile[Position])
   {case '#' : return true;
    case '&' : return true;
    case '\'' : return true;
    case '(' : return true;
    case '+' : return true; //kommt häufig in URL als Leerzeichenersatz vor
    case '-' : return true;
    case '/' : return true;
    case '>' : return true;
    case '@' : return true;
    case '[' : return true;
    case '\\' : return true;
    case '_' : return true;
    case '`' : return true; //Gravis, wird manchmal als Apostophersatz verwendet
    case '{' : return true;
    case '|' : return true;
    case '}' : return true;
    case static_cast<char>(-62) :
      if (Zeile.size()<=Position+1)
       {cerr << "Das Byte C2 taucht am Zeilenende auf und verstößt damit gegen die UTF-8-Kodierung!" << endl;
       }
      else
       {if (Zeile[Position+1]==static_cast<char>(-65)) //betrifft ¿
         {return true;
         }
       }
      break;
    case static_cast<char>(-30) :
      if (Zeile.size()<=Position+2)
       {cerr << "Das Byte E2 taucht so weit am Zeilenende auf, dass keine zwei Bytes mehr dahinter stehen, was einen Verstoß gegen die UTF-8-Kodierung darstellt!" << endl;
       }
      else
       {if (Zeile[Position+1]==static_cast<char>(-128))
         {if ((Zeile[Position+2]>=static_cast<char>(-112))&&(Zeile[Position+2]<=static_cast<char>(-107))) //betrifft ein paar horizontale, vertikal zentrierte Striche
           {return true;
           } 
          if ((Zeile[Position+2]==static_cast<char>(-102))||(Zeile[Position+2]==static_cast<char>(-98))) //betrifft Anführungszeichen unten
           {return true;
           }
         }
       }
      break;
    case '%' : //um Prozentkodierung in den URL von Weblinks korrekt einzuordnen
      if (Zeile.size()>Position+2)
       {if (Zeile[Position+1]=='2')
         {if (Zeile[Position+2]=='0') //um %20 zu erkennen, was in URL als Umschrift für Leerzeichen verwandt wird
           {return true;
           }
          if (Zeile[Position+2]=='1') //um %21 zu erkennen, was in URL als Umschrift für ! verwandt wird
           {return true;
           }
          if (Zeile[Position+2]=='2') //um %22 zu erkennen, was in URL als Umschrift für " verwandt wird
           {return true;
           }
          if (Zeile[Position+2]=='8') //um %28 zu erkennen, was in URL als Umschrift für ( verwandt wird
           {return true;
           }
          if (Zeile[Position+2]=='9') //um %22 zu erkennen, was in URL als Umschrift für ) verwandt wird
           {return true;
           }
          if ((Zeile[Position+2]=='c')||(Zeile[Position+2]=='C')) //um %2C zu erkennen, was in URL als Umschrift für das Komma (,) verwandt wird
           {return true;
           }
         }
        else
         {if (Zeile[Position+1]=='3')
           {if ((Zeile[Position+2]=='a')||(Zeile[Position+2]=='A')) //um %3A zu erkennen, was in URL als Umschrift für den Doppelpunkt (:) verwandt wird
             {return true;
             }
            if ((Zeile[Position+2]=='b')||(Zeile[Position+2]=='B')) //um %3B zu erkennen, was in URL als Umschrift für ; verwandt wird
             {return true;
             }
            if ((Zeile[Position+2]=='f')||(Zeile[Position+2]=='F')) //um %3F zu erkennen, was in URL als Umschrift für ? verwandt wird
             {return true;
             }
           }
         }
       }
      break;
    default : return false;
   }
  return false;
 }
 
bool wortbeendendesZeichenrestriktiv (const string & Zeile, const string::size_type & Position) //prüft, ob im String an der übergebenen Position eine UTF-8-Kette kommt, die für ein explizit als wortbeendendes Zeichen bekanntes Zeichen steht
//Die übergebene Position muss im String ansprechbar sein, sonst gibt's Speicherbereichsverletzungen. Dieser Fall muss vor Aufruf ausgeschlossen sein.
 {switch (Zeile[Position])
   {case '\t' : return true; //Tabulatorzeichen
    case ' ' : return true; //Leerzeichen
    case '!' : return true;
    case '"' : return true;
    case ')' : return true;
    case ',' : return true;
    case '.' : return true;
    case ':' : return true;
    case ';' : return true;
    case '<' : return true;
    case '?' : return true;
    case ']' : return true;
    case static_cast<char>(-62) :
      if (Zeile.size()<=Position+1)
       {cerr << "Das Byte C2 taucht am Zeilenende auf und verstößt damit gegen die UTF-8-Kodierung!" << endl;
       }
      else
       {if (Zeile[Position+1]==static_cast<char>(-96)) //betrifft das geschützte Leerzeichen
         {return true;
         }
        if (Zeile[Position+1]==static_cast<char>(-85)) //betrifft «
         {return true;
         }
        if (Zeile[Position+1]==static_cast<char>(-69)) //betrifft »
         {return true;
         }
       }
      break;
    case static_cast<char>(-30) :
      if (Zeile.size()<=Position+2)
       {cerr << "Das Byte E2 taucht so weit am Zeilenende auf, dass keine zwei Bytes mehr dahinter stehen, was einen Verstoß gegen die UTF-8-Kodierung darstellt!" << endl;
       }
      else
       {if (Zeile[Position+1]==static_cast<char>(-128))
         {if (Zeile[Position+2]==static_cast<char>(-128)) //betrifft das Leerzeichen EN QUAD
           {return true;
           }
          if ((Zeile[Position+2]>=static_cast<char>(-127))&&(Zeile[Position+2]<=static_cast<char>(-119))) //betrifft ein paar Leerzeichen
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-104)) //betrifft ‘
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-103)) //betrifft ’
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-101)) //betrifft noch ein hochgestelltes, einstrichiges Anführungszeichen
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-100)) //betrifft “
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-99)) //betrifft ”
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-97)) //betrifft noch ein hochgestelltes, zweistrichiges Anführungszeichen
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-90)) //betrifft Auslassungspunkte
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-81)) //betrifft das schmale geschützte Leerzeichen
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-71)) //betrifft ‹
           {return true;
           }
          if (Zeile[Position+2]==static_cast<char>(-70)) //betrifft ›
           {return true;
           }
         }
        else
         {if (Zeile[Position+1]==static_cast<char>(-127))
           {if ((Zeile[Position+2]>=static_cast<char>(-121))&&(Zeile[Position+2]<=static_cast<char>(-119))) //betrifft drei Kombinationen aus Ausrufe- und Fragezeichen
             {return true;
             }
           }
         }
       }
      break;
    default : return false;
   }
  return false;
 }

vector<string> liesWoerterausDatei (const string & Dateiname) //schreibt die Zeilen einer UTF-8-kodierten Datei in die Einträge eines "Vektors"
 {vector<string> Stringliste(0);
  ifstream Datei;
  Datei.open(Dateiname.c_str());
  if (!Datei.good())
   {cerr << "Beim Öffnen der Datei " << Dateiname << " ist ein Fehler aufgetreten!" << endl;
   }
  while (Datei.good())
   {string Zeile;
    getline(Datei, Zeile); //schreibt eine Zeile der Datei in die Variable Zeile
    Stringliste.push_back(Zeile);
   }
  Datei.close();
  if (Stringliste.size()>0) //zur Entfernung der ersten drei Bytes "", die markieren, dass eine Datei in UTF-8 kodiert ist
   {if (Stringliste[0].size()>=3)
     {if ((Stringliste[0][0]==static_cast<char>(-17))&&(Stringliste[0][1]==static_cast<char>(-69))&&(Stringliste[0][2]==static_cast<char>(-65))) //fragt ab, ob die ersten drei Bytes "" sind
       {Stringliste[0]=Stringliste[0].substr(3,Stringliste[0].size()-3);
       }
     }
   }
  return Stringliste;
 }

string schreibeZiffer(const long long int & z) //wandelt eine ganze Zahl von 0 bis 9 in einen String, der genau diese Zahl enthält
 {string Ziffer;
  switch (z)
   {case 1 : Ziffer = "1"; break;
    case 2 : Ziffer = "2"; break;
    case 3 : Ziffer = "3"; break;
    case 4 : Ziffer = "4"; break;
    case 5 : Ziffer = "5"; break;
    case 6 : Ziffer = "6"; break;
    case 7 : Ziffer = "7"; break;
    case 8 : Ziffer = "8"; break;
    case 9 : Ziffer = "9"; break;
    case 0 : Ziffer = "0"; break;
    default : cerr << "Der Ziffernmacher stößt auf was Unziffriges!" << endl;
   }
  return Ziffer;
 }

string schreibeZahlmitPunkten (long long int z) //wandelt eine ganze Zahl in einen String mit Tausenderpunkten
 {string Zahl=""; //für die Ausgabe
  if (z==0)
   {Zahl="0";
   }
  else
   {if (z<0)
     {Zahl="-"+schreibeZahlmitPunkten(-1*z);
     }
    else
     {int j=0;
      while (z>0)
       {if (j==3)
         {Zahl = "."+Zahl;
          j=0;
         }
        Zahl = schreibeZiffer(z%10)+Zahl;
        z/=10;
        j++;
       }
     }
   }
  return Zahl;
 }

bool Suchschluesselpruefer (const vector<string> & Suchschluessel) //gibt true zurück, wenn der "Vektor" mit den Suchschlüsseln nicht leer ist und kein Suchschlüssel leer ist, sonst false
 {if (Suchschluessel.size()>0)
   {for (vector<string>::size_type i=0; i<Suchschluessel.size(); i++)
     {if (Suchschluessel[i].size()==0)
       {return false;
       }
     }
   }
  else
   {return false;
   }
  return true;
 }

vector< vector<long int> > Namensucher (const string & Zeile, const vector<string> & Suchschluessel)
//durchsucht den im ersten Argument gegebenen String nach allen im "Vektor" angegebenen Strings und gibt eine
//Liste aus acht verschiedenen Trefferlisten zurück;
//jede Trefferliste ist ein "Vektor", der in Eintrag i die Anzahl der Stellen enthält, an denen Suchschluessel i passend gefunden wurde;
//in der ersten Trefferliste sind alle Vorkommen der gesuchten Zeichenfolge gezählt;
//in der zweiten nur die Vorkommen, in denen der Suchstring nicht von einem anderen Buchstaben als "s" gefolgt
//wird; wird der Suchstring von einem s gefolgt, wird das aber nur gezählt, wenn dahinter kein Buchstabe kommt; beispielsweise
//würde dort für den Suchstring "Vera" ein "Vera " oder "Veras " als Treffer verbucht, "Verantwortung" oder
//"Veraschung" aber nicht; wenn ein Suchwort auf "s", "x", oder "z" endet, werden auch Vorkommen, bei denen
//das Suchwort von einem Extra-s gefolgt wird, nicht gezählt, z.B. würde "Andreass" nicht als Suchtreffer für
//"Andreas" verbucht;
//in der dritten Trefferliste werden nur die Suchtreffer gezählt, in denen das Suchwort von keinem
//Buchstaben gefolgt wird, z.B. würde dort für das Suchwort "Andrea" ein Vorkommen von "Andreas" nicht als
//Treffer gezählt;
//die vierte Trefferliste ist wie die zweite und die fünfte wie die dritte, nur dass bei der vierten und fünften
//nicht darauf geachtet wird, dass kein als Buchstabe bekanntes Zeichen folgt, sondern darauf, dass ein als
//wortbeendendes Zeichen bekanntes Zeichen folgt;
//die sechste und siebte Trefferliste sind wie die vierte und fünfte, nur dass weniger Zeichen als wortbeendend gezählt werden;
//die achte Trefferliste enthält nur die Anzahl der Treffer, an denen das Suchwort gefolgt von einem Leerzeichen (dem einen wahren ASCII-Leerzeichen) gefunden wurde, sonst keine;
//benötigt, dass es mindestens einen zu suchenden String gibt und alle zu suchenden Strings nicht leer sind; wenn der
//Vektor mit den zu suchenden Strings leer ist, werden leere Ergebnislisten zurückgegeben; wenn ein zu suchender
//String leer ist, wird dieser an allen Positionen des zu durchsuchenden Strings gefunden; wird der leere String
//durchsucht, wird in diesem der leere String nicht gefunden, weil er keine Positionen hat
 {vector< vector<long int> > Trefferlisten(8); //für die Ausgabe
  Trefferlisten[0].resize(Suchschluessel.size()); //Zeichenfolgentrefferlisten
  Trefferlisten[1].resize(Suchschluessel.size()); //NamensSTrefferlisten
  Trefferlisten[2].resize(Suchschluessel.size()); //NamensOhneSTrefferlisten
  Trefferlisten[3].resize(Suchschluessel.size()); //S
  Trefferlisten[4].resize(Suchschluessel.size());
  Trefferlisten[5].resize(Suchschluessel.size()); //S
  Trefferlisten[6].resize(Suchschluessel.size());
  Trefferlisten[7].resize(Suchschluessel.size());
  for (vector<long int>::size_type i=0; i<Trefferlisten[0].size(); i++)
   {Trefferlisten[0][i]=0;
    Trefferlisten[1][i]=0;
    Trefferlisten[2][i]=0;
    Trefferlisten[3][i]=0;
    Trefferlisten[4][i]=0;
    Trefferlisten[5][i]=0;
    Trefferlisten[6][i]=0;
    Trefferlisten[7][i]=0;
   }
  for (vector<string>::size_type i=0; i<Suchschluessel.size(); i++)
   {string::size_type n=0;
    while (n<Zeile.size())
     {n=Zeile.find(Suchschluessel[i], n);
      if (n!=string::npos)
       {Trefferlisten[0][i]++;
        if (Suchschluessel[i].size() + n == Zeile.size()) //ist erfüllt, wenn die gefundene Zeichenfolge am Zeilenende steht
         {Trefferlisten[1][i]++;
          Trefferlisten[2][i]++;
          Trefferlisten[3][i]++;
          Trefferlisten[4][i]++;
          Trefferlisten[5][i]++;
          Trefferlisten[6][i]++;
         }
        else //sonst muss geprüft werden, ob der Fund nicht ein Anfangsstück eines anderen Wortes ist
         {if ((Suchschluessel[i][Suchschluessel[i].size() - 1]=='s')||(Suchschluessel[i][Suchschluessel[i].size() - 1]=='x')||(Suchschluessel[i][Suchschluessel[i].size() - 1]=='z')) //ist erfüllt, wenn das letzte Zeichen des Suchworts ein s, x oder z ist
           {if (!Buchstabe(Zeile, Suchschluessel[i].size() + n))//ist erfüllt, wenn das erste Zeichen nach dem Suchschlüssel kein Buchstabe ist
             {Trefferlisten[1][i]++;
              Trefferlisten[2][i]++;
             }
            if (Zeile[Suchschluessel[i].size() + n]==' ') //ist erfüllt, wenn das Wort von einem Leerzeichen gefolgt wird
             {Trefferlisten[3][i]++;
              Trefferlisten[4][i]++;
              Trefferlisten[5][i]++;
              Trefferlisten[6][i]++;
              Trefferlisten[7][i]++;
             }
            else
             {if (wortbeendendesZeichenrestriktiv(Zeile, Suchschluessel[i].size() + n))
               {Trefferlisten[3][i]++;
                Trefferlisten[4][i]++;
                Trefferlisten[5][i]++;
                Trefferlisten[6][i]++;
               }
              else
               {if (weitereswortbeendendesZeichen(Zeile, Suchschluessel[i].size() +n))
                 {Trefferlisten[3][i]++;
                  Trefferlisten[4][i]++;
                 }
               }
             }
           }
          else //hier ist das letzte Zeichen des Suchschlüssels kein s, x oder z
           {if (Zeile[Suchschluessel[i].size() + n]=='s') //hier wird das Suchwort in der Zeile von einem s gefolgt
             {if (Suchschluessel[i].size() + n + 1 == Zeile.size()) //ist erfüllt, wenn das Suchwort gefolgt von einem s am Zeilenende steht
               {Trefferlisten[1][i]++;
                Trefferlisten[3][i]++;
                Trefferlisten[5][i]++;
               }
              else
               {if (!Buchstabe(Zeile, Suchschluessel[i].size() + n + 1))
                 {Trefferlisten[1][i]++;
                 }
                if (wortbeendendesZeichenrestriktiv(Zeile, Suchschluessel[i].size() + n + 1))
                 {Trefferlisten[3][i]++;
                  Trefferlisten[5][i]++;
                 }
                else
                 {if (weitereswortbeendendesZeichen(Zeile, Suchschluessel[i].size() + n + 1))
                   {Trefferlisten[3][i]++;
                   }
                 }
               }
             }
            else //hier wird das Suchwort in der Zeile von keinem s gefolgt
             {if (!Buchstabe(Zeile, Suchschluessel[i].size() + n))//ist erfüllt, wenn das erste Zeichen nach dem Suchschlüssel kein Buchstabe ist
               {Trefferlisten[1][i]++;
                Trefferlisten[2][i]++;
               }
              if (Zeile[Suchschluessel[i].size() + n]==' ')
               {Trefferlisten[3][i]++;
                Trefferlisten[4][i]++;
                Trefferlisten[5][i]++;
                Trefferlisten[6][i]++;
                Trefferlisten[7][i]++;
               }
              else
               {if (wortbeendendesZeichenrestriktiv(Zeile, Suchschluessel[i].size() + n))
                 {Trefferlisten[3][i]++;
                  Trefferlisten[4][i]++;
                  Trefferlisten[5][i]++;
                  Trefferlisten[6][i]++;
                 }
                else
                 {if (weitereswortbeendendesZeichen(Zeile, Suchschluessel[i].size() + n))
                   {Trefferlisten[3][i]++;
                    Trefferlisten[4][i]++;
                   }
                 }
               }
             }
           }
         }    
        n++;
       }
     }
   }
  return Trefferlisten;
 }

vector< vector <long long int> > Dateienumfassenderdurchsuchungsprozedur (const string & Dateiname, const vector<string> & Suchschluessel)
//durchsucht jede Zeile einer Datei mit Hilfe der obigen Funktion Namensucher;
//gibt einen "Vektor" mit acht Ergebnislisten zurück, die aber nicht die
//Positionen der Funde, sondern die Anzahl der Funde beinhalten;
//die Zuordnung der Ergebnislisten ist wie bei der obigen Funktion Namensucher
 {vector<long long int> Zeichenfolgentrefferzahl(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlS(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlOhneS(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlnachAusschlussS(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlnachAusschlussOhneS(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlnachAusschlussSrestriktiv(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlnachAusschlussOhneSrestriktiv(Suchschluessel.size()); //für den Anfang alle =0
  vector<long long int> TrefferzahlnurvorLeerzeichen(Suchschluessel.size()); //für den Anfang alle =0
  for (vector<string>::size_type i=0; i<Suchschluessel.size(); i++)
   {Zeichenfolgentrefferzahl[i]=0;
    TrefferzahlS[i]=0;
    TrefferzahlOhneS[i]=0;
    TrefferzahlnachAusschlussS[i]=0;
    TrefferzahlnachAusschlussOhneS[i]=0;
    TrefferzahlnachAusschlussSrestriktiv[i]=0;
    TrefferzahlnachAusschlussOhneSrestriktiv[i]=0;
    TrefferzahlnurvorLeerzeichen[i]=0;
   }
  if (Suchschluesselpruefer(Suchschluessel)==true)
   {ifstream Datei;
    Datei.open(Dateiname.c_str());
    if (!Datei.good())
     {Datei.close();
      cerr << "Beim Öffnen der Datei " << Dateiname << " ist ein Fehler aufgetreten!" << endl;
     }
    else
     {while (Datei.good())
       {string Zeile;
        getline(Datei, Zeile); //schreibt eine Zeile der Datei in die Variable Zeile
        vector< vector<long int> > Zeilentrefferlisten = Namensucher(Zeile, Suchschluessel);
        for (int Suchschluesselnummer=0; Suchschluesselnummer<Suchschluessel.size(); Suchschluesselnummer++)
         {Zeichenfolgentrefferzahl[Suchschluesselnummer]+=Zeilentrefferlisten[0][Suchschluesselnummer];
          TrefferzahlS[Suchschluesselnummer]+=Zeilentrefferlisten[1][Suchschluesselnummer];
          TrefferzahlOhneS[Suchschluesselnummer]+=Zeilentrefferlisten[2][Suchschluesselnummer];
          TrefferzahlnachAusschlussS[Suchschluesselnummer]+=Zeilentrefferlisten[3][Suchschluesselnummer];
          TrefferzahlnachAusschlussOhneS[Suchschluesselnummer]+=Zeilentrefferlisten[4][Suchschluesselnummer];
          TrefferzahlnachAusschlussSrestriktiv[Suchschluesselnummer]+=Zeilentrefferlisten[5][Suchschluesselnummer];
          TrefferzahlnachAusschlussOhneSrestriktiv[Suchschluesselnummer]+=Zeilentrefferlisten[6][Suchschluesselnummer];
          TrefferzahlnurvorLeerzeichen[Suchschluesselnummer]+=Zeilentrefferlisten[7][Suchschluesselnummer];
         }
       }
      Datei.close();
      for (vector<string>::size_type Suchschluesselnummer=0; Suchschluesselnummer<Suchschluessel.size(); Suchschluesselnummer++)    
       {if (Zeichenfolgentrefferzahl[Suchschluesselnummer]==0)
         {cout << "Das Suchwort \"" << Suchschluessel[Suchschluesselnummer] << "\" wurde in der Datei " << Dateiname << " nicht gefunden.\n";
         }
        else
         {if (Zeichenfolgentrefferzahl[Suchschluesselnummer]>1)
           {cout << "Das Suchwort \"" << Suchschluessel[Suchschluesselnummer] << "\" wurde in der Datei " << Dateiname << " an " << schreibeZahlmitPunkten(Zeichenfolgentrefferzahl[Suchschluesselnummer]) << " Stellen gefunden.\n";
           }
          else //hier gab es in der Datei genau einen Suchtreffer
           {cout << "Das Suchwort \"" << Suchschluessel[Suchschluesselnummer] << "\" wurde in der Datei " << Dateiname << " an genau einer Stelle gefunden.\n";
           }
         }
       }
     }
   } 
  else
   {cout << "Es gibt kein Suchwort oder ein Suchwort ist leer!" << endl;
   } 
  vector <vector <long long int> > Ergebnislisten(8);
  Ergebnislisten[0]=Zeichenfolgentrefferzahl;
  Ergebnislisten[1]=TrefferzahlS;
  Ergebnislisten[2]=TrefferzahlOhneS;
  Ergebnislisten[3]=TrefferzahlnachAusschlussS;
  Ergebnislisten[4]=TrefferzahlnachAusschlussOhneS;
  Ergebnislisten[5]=TrefferzahlnachAusschlussSrestriktiv;
  Ergebnislisten[6]=TrefferzahlnachAusschlussOhneSrestriktiv;
  Ergebnislisten[7]=TrefferzahlnurvorLeerzeichen;
  return Ergebnislisten;
 }
 
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 Namensliste=argv[1];
    string Eingabedateiname=argv[2];
    string Ausgabedateiname=argv[3];
    cout << "Das Programm geht davon aus, dass die Namensliste und die zu durchsuchende Datei in UTF-8 kodiert sind." << endl;
    vector<string> Suchschluessel = liesWoerterausDatei (Namensliste);
    if (Suchschluesselpruefer(Suchschluessel)==true)
     {cout << "Die Namensliste wurde eingelesen und der Suchvorgang wird gestartet.\n";
      cout << endl;
      vector< vector<long long int> > Ergebnislisten = Dateienumfassenderdurchsuchungsprozedur(Eingabedateiname, Suchschluessel);
      cout << '\n';
      cout << "Der Suchvorgang wurde abgeschlossen. Das Schreiben der Ausgabedatei wird begonnen." << endl;
      ofstream Datei;
      Datei.open(Ausgabedateiname.c_str());
      if (!Datei.good())
       {Datei.close();
        cerr << "Beim Öffnen der Ausgabedatei " << Ausgabedateiname << " ist ein Fehler aufgetreten." << endl;
       }
      else
       {Datei << static_cast<char>(-17) << static_cast<char>(-69) << static_cast<char>(-65); //um anzuzeigen, dass die Ausgabedatei in UTF-8 kodiert ist
        for (int i=0; i<Ergebnislisten[0].size(); i++)
         {Datei << Suchschluessel[i] << "\t\t" << Ergebnislisten[0][i] << '\t' << Ergebnislisten[1][i] << '\t' << Ergebnislisten[2][i] << '\t' << Ergebnislisten[3][i] << '\t' << Ergebnislisten[4][i] << '\t' << Ergebnislisten[5][i] << '\t' << Ergebnislisten[6][i] << '\t' << Ergebnislisten[7][i] << '\n';
         }
        Datei.close();
        cout << '\n';
        cout << "Die Ausgabe befindet sich in der Datei " << Ausgabedateiname << "." << endl;
       }
     }
    else
     {cout << "Die Datei mit der Namensliste enthält eine Leerzeile oder es konnte kein Name eingelesen werden!" << endl;
     }
   }
  else //hier wurden dem Programm zu wenige Parameter übergeben
   {cout << "Dem Programm müssen beim Aufruf drei Dateinamen übergeben werden:\n";
    cout << "Als erstes den Namen der Datei mit der Namensliste, als zweites die nach den Namen zu durchsuchende Datei und als drittes den Namen der Datei, in die die Ausgabe geschrieben werden soll.\n";
    cout << "Falls eine Datei mit dem Namen der Ausgabedatei bereits existiert, wird diese einfach ersetzt, ansonsten wird eine Datei dieses Namens angelegt.\n";
    cout << "Sowohl die Datei mit der Namensliste als auch die zu durchsuchende Datei müssen in UTF-8 kodiert sein.\n";
    cout << "Die Datei mit der Namensliste darf keine Leerzeilen enthalten und in jeder Zeile muss genau ein Name stehen.\n";
    cout << "Ein korrekter Programmaufruf sieht beispielsweise so aus:\n";
    cout << "\"" << argv[0] << " Namensliste.txt Eingabe.txt Ausgabe.txt\"" << endl;
   }
  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;
 }