# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik: Social Media Korpora

# data[].attributes.body
API

# data[].attributes.body
YouTube Data Tools
rtweet
TAGS
Instaloader

# data[].attributes.attachment_link.readable_text
Übung 1:

Suchausdruck: ^(.+?) ([A-Z]+) .+? (\d+\.\d+\.\d+).+?(\S+@\S+)
Ersetzen durch: \2, \1\t\3\t\4

-------

Übung 2:

I)
Suchausdruck: https://www\.(.+?)\.de/(.+?)/(.+?)/(\d+)/(.+?/analyse)/(.+?)/(.+?)$
Ersetzen durch: <text source="\1" league="\2" season="\3" round="\4" team1="\6" team2="\7" url="https://www.\1.de/\1/\2/\3/\4/\5/\6/\7">

II)
Suchausdruck: ^(\w.+?)$
Ersetzen durch: <p>\1</p>

-------

Übung 3:

Suchausdruck: /spielbericht/.+?"

# data[].attributes.body
Encoding, Webscraping, Sitemap

# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik: pdf to text und Webscraping

# data[].attributes.body
pdftotext
trafilatura

# data[].attributes.body
XML, Markups, TEI

# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik: SketchEngine und XML

# data[].attributes.body
SketchEngine

# data[].attributes.body
Kühner 2017

# data[].attributes.body
Stopwörter, Referenzkorpus

# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik: Analysetools für eigene Korpora

# data[].attributes.body
AntConc
Voyant Tools
Leipzig Corpora Collection

# data[].attributes.body
Texteditor Sublime und Notepad++

# data[].attributes.attachment_link.readable_text
Übung Korpuslinguistik: Regular Expressions

Regular Expressions für die Korpuslingustik

Hinter den "Regular Expressions" (Reguläre Ausdrücke) verbirgt sich eine mächtige Sprache, um bestimmte Such- und Ersetzungsmuster zu definieren. Man kann damit z.B. aus einer Textdatei alle URLs (Adressen) von Webseiten extrahieren, ohne genau zu wissen, was für Adressen da stehen werden. Oder man kann nach Wörtern suchen, die mit einem Grossbuchstaben beginnen und mit -en enden. Oder man kann aus einem Text eine Wortliste erstellen, wo jedes Wort und jedes Satzzeichen auf einer eigenen Zeile steht.

Hier werden nur ganz grundlegende Regeln der Regular Expressions gezeigt, sowie einige Muster, die für typische korpuslinguistische Bedürfnisse gebraucht werden. Eine Einführung bietet aber z.B. die Seite www.regular-expressions.info, die Unixfibel, der Wikipedia-Eintrag, sowie viele andere Quellen, z.B. auch die Online-Hilfe des Texteditors BBEdit für Mac OS X. BBEdit bietet sich beispielsweise an, um mit regulären Ausdrücken zu arbeiten. Im normalen "Find & Replace"-Funktion kann man damit arbeiten, wenn man das Häkchen "Use Grep" setzt ("grep" war eines der ersten Programme, das mit regulären Ausdrücken umgehen konnte; der Name wird deshalb oft synonym mit den regular expressions verwendet).

In einem Kapitel zum Import von Korpora in die Datenbank Filemaker wird an einem Beispiel gezeigt, wie mit regulären Ausdrücken eine kommaseparierte Textdatei erstellt werden kann.

Grundlagen

Grundsätzlich bedeuten im Suchmuster die Buchstaben sich selbst. Das Suchmuster

Hund

findet also alle Vorkommen von "Hund". Weiter können z.B. folgende Zeichen verwendet werden:

Hund.*
findet: Hund, Hunde, Hundes, Hundeschnauze, Hundeball etc.

H.nd
findet: Hund, Hand, Hind, Hond, Hend

Hund.+
findet: Hunde, Hundes, Hundeschnauze etc., aber nicht Hund

Hund.?
findet: Hund, Hunde, aber nicht Hundeschnauze, Hundes etc.

Oder ein etwas komplizierteres Beispiel:

http://(www\.)?.+?\.com

Dieses Suchmuster findet Internetadressen der Art http://www.irgendwas.com. Das "www" muss nicht stehen, kann aber. Und wir suchen nur nach Endungen mit .com. Folgende Besonderheiten sind ersichtlich:

Gewisse Zeichen, die innerhalb der Syntax der regulären Ausdrücke auch eine andere Bedeutung haben, müssen "maskiert" werden. Man setzt einen Rückwärts-Schrägstrich davor (\). Das gilt z.B. für den Punkt, der ja, wie oben beschrieben, für ein beliebiges Zeichen steht. Wenn wir nun in unserem Suchmuster aber wirklich einen Punkt meinen, müssen wir das mit "\." umschreiben. Gleiches gilt für den Schrägstrich.

Mit Klammerungen kann man Gruppen bilden. Das "www", gefolgt von einem Punkt, ist zu einer Gruppe zusammengefasst. Nach der ganzen Gruppe steht ein Fragezeichen; das bedeutet also, dass diese Gruppe 0 oder einmal vorkommt.

Spezielle Zeichen

Klassen: Was in eckigen Klammern steht, definiert eine Klasse an Zeichen. Im Beispiel findet die Klammer einen Buchstaben, der entweder a, e, i, o oder u ist. Man kann die Klassen auch so angeben:

Damit wird ein Buchstabe, gross oder klein, von a-z gefunden. Das folgende Suchmuster

Renntier[es]

findet also "Renntiere" oder "Renntiers". Stünde hinter der Klammer noch ein Fragezeichen, würde auch "Renntier" gefunden.

Zahlen 0-9.
Möchte man also z.B. vierstellige Jahreszahlen finden, schreibt man einfach:

Möchte man hingegen Zahlen beliebiger Länge finden, schreibt man:

Suchen und ersetzen

Auf alles, was in normale Klammern gesetzt wird, kann nach der Suche zurückgegriffen werden. Ein Beispiel: Ich habe einen Text, in dem Datumsangaben der Art "2005-10-1" vorkommen. Ich möchte aber diese so formatiert haben: "01. 10. 2005". Dann formuliere ich zuerst ein Suchmuster, das Daten der ersten Art findet:

Damit finde ich zuverlässig diese Datumsangaben, egal ob die Zahlen für Monat und Tag ein- oder zweistellig sind. Jetzt muss ich ja aber das Gefundene umformen. Dazu setze ich erstmal Klammern um das Suchmuster, denn alles was in Klammern steht, wird im Speicher behalten:

Mich interessieren nur die Zahlen, die Bindestriche dazwischen können wir gleich wieder vergessen. Der Clou ist jetzt, dass ich als Ersetzung mit \1, \2, \3 etc. auf den Inhalt der Klammern zurückgreifen kann. \1 ist der Inhalt der ersten, \2 der zweiten Klammer etc. Als Ersetzungsmuster schreibe ich also:

Damit wird das Datum jeweils umformatiert. Beachte: Der Punkt wird hier wieder mit einem umgekehrten Schrägstrich maskiert, damit klar ist, dass es sich beim Punkt nicht um den Punkt handelt, der für ein beliebiges Zeichen steht, sondern tatsächlich um einen Punkt.

Mit dieser Methode kann man ganz schnell einen Text, der irgendwie falsch formatiert ist, umformatieren. Z.B. wenn ich eine Wortliste haben, wo immer hinter dem Wort eine Zahl steht, z.B. die Frequenz, mit der das Wort auftaucht, und ich aus irgendwelchen Gründen die Zahl aber vor dem Wort stehen haben möchte, dann kann ich das mit einer solchen Suchen-und-Ersetzen-Aktion schnell erledigen.

Nützliche Muster für die Korpuslinguistik

Wortlisten erstellen

Um aus normalem Text Wortlisten zu erstellen, bei denen pro Zeile nur ein Wort oder Satzzeichen steht, kann man mit regulären Ausdrücken so vorgehen: Zuerst ersetzen wir alle Arten von Leerzeichen (egal, wie oft sie hintereinander vorkommen) durch Zeilenschaltungen:

Suche nach:     \s+
Ersetzen durch: \r

Dann trennen wir alle Arten von Interpunktion durch Zeilenschaltungen. Gewisse Versionen von regulären Ausdrücken verstehen das Suchmuster [:punct:] um alle Interpunktionszeichen zu finden. Jene, die in BBEdit implementiert ist, leider nicht. Dann muss man halt eine Klasse von Interpunktionszeichen erstellen (und dabei das Maskieren der wörtlich zu verstehenden Zeichen nicht vergessen!):

Suche nach:     ([\.,:;!\?"'\(\)\[\]\-/])
Ersetzen durch: \r\1\r

Zu beachten: Die Klasse ist definiert durch die eckigen Klammern, zusätzlich werden normale Klammern verwendet, mit denen der tatsächlich gefundene Inhalt gemerkt wird, damit mit \1 beim Ersetzen wieder darauf zurückgegriffen werden kann.

Jetzt ist es noch so, dass teilweise zwei Zeilenschaltungen hintereinander vorkommen (nach Interpunktionszeichen). Diese können wir so eliminieren:

Suche nach:     \r+
Ersetzen durch: \r

Fertig ist die Wortliste!

# data[].attributes.attachment_link.title
Noah Bubenhofer: Einführung in die Korpuslinguistik (RegExp)

# data[].attributes.attachment_link.description
Übung Korpuslinguistik: Regular Expressions Hinter den "Regular Expressions" (Reguläre Ausdrücke) verbirgt sich eine mächtige Sprache, um bestimmte Such- und Ersetzungsmuster zu definieren. Man kann damit z.B. aus einer Textdatei alle URLs (Adressen) von Webseiten extrahieren, ohne genau zu wissen, was für Adressen da stehen werden.

# data[].attributes.attachment_link.readable_text
"Reguläre Ausdrücke" sind eine Art Sprache, die beim Programmieren für diverse Problemlösungen verwendet werden kann, insbesondere dann, wenn es darum geht, Zeichenketten (Strings) zu bearbeiten, zu prüfen oder in ihnen etwas zu suchen.

Und weil der Name "Reguläre Ausdrücke" etwas unhandlich ist, heißen die "Regular Expressions" auch oft einfach nur "RegEx(en)".

Hier gibts ein kleines Tutorial zu diesen esoterisch anmutenden aber unglaublich mächtigen Zeichenketten, die beim unbedarften Betrachter Assoziationen eines kleinen Kindes und dessen ersten Versuchen an der Tastatur auslösen.

Einführung

Was sind reguläre Ausdrücke? Wie gesagt, können mit regulären Ausdrücken Zeichenketten auf eine bestimmte Zusammensetzung geprüft werden, wie dies z.B. bei Anwendungen wichtig ist, die Eingaben eines Benutzers erwarten.

Beispiele:

Konkrete Anwendung

Populäre Stellen, an denen man auf reguläre Ausdrücke stoßen kann und an denen du dieses Wissen anbringen kannst:

Web-Anwendungen (z.B. in PHP, Perl)

Unix-Skripte

Im Folgenden wird davon ausgegangen, dass du eine entsprechende Stelle vorliegen hast und dich bereits informiert hast, wie reguläre Ausdrücke in der Umgebung deiner Wahl verwendet werden. In PHP geschieht dies z.B. mit den Funktionen preg_match (Prüfen von Strings, finden von Zeichenketten) und preg_replace (Ersetzen von Zeichenketten).

Am besten testest du deinen Ausdruck zuerst "auf dem Trockenen". Dazu kannst du ein Onlinetool wie regex 101 oder den Regex-Coach benutzen.

Guter Stil

Oft wirst du feststellen, dass es zu einem Problem viele verschiedene Lösungen gibt. Dabei stehen sich dann gegenüber

Eine genaue Lösung und eine allgemeinere. Besteht die Gefahr, dass die genaue Lösung zu "restriktiv" ist (und damit vielleicht ein Benutzer frustriert aufgibt, wenn seine richtige Eingabe nicht akzeptiert wird), nimm lieber die allgemeine. Ist das Problem aber sehr "eindeutig", nimm lieber die genaue.

Eine genaue Lösung und eine schnelle. Lange reguläre Ausdrücke können viel Zeit für die Verarbeitung in Anspruch nehmen. Auch hier musst du abwägen, was dir wichtiger ist. Es könnte Benutzer ärgern, wenn die Prüfung einer Eingabe sehr lange dauert.

Eine einfache Lösung und eine "elegante". Du kannst in 10 Zeichen ausdrücken, was andere nicht in 60 schaffen? Prima, aber denk dran dass du vielleicht später nochmal was ändern willst! Und das kann schwerer werden, als man denkt. Daher nimm lieber die einfachere Variante oder kommentiere komplizierte Ausdrücke.

Probleme

Wenn dein regulärer Ausdruck Probleme macht, du dir aber ziemlich sicher bist dass er richtig ist, schau dir zunächst mal weiter unten die Sonderzeichen an und was man mit ihnen machen muss, um sie zu benutzen. Dabei sind Sonderzeichen schon so "banale" Sachen wie ein Punkt.

Außerdem solltest du ihn dringend im o.g. RegEx-Coach testen! Dieser fördert manchmal geradezu unglaubliche irreguläre Verhaltensweisen zutage ;-)

Dann darfst du als nächstes Google benutzen ;-) Da findet man sehr viele Informationen, viele Vorlagen um diverse Problemstellungen regulär auszudrücken und auch die Google-Groups sind immer hilfreich.

Konventionen

In diesem Tutorial werden folgende Farben für Texte (Strings) verwendet: Regulärer Ausdruck, den du theoretisch so verwenden kannst, mit
markierten neuen Teilen
Absichtlich fehlerhafter Code Öfters verwende ich mal den Programmierern nicht fremden "String" (ich spar mir den Unterwäsche-Witz) für ein Textstück. Außerdem gilt: ein Ausdruck "trifft" = "beschreibt einen Text genau" = "erledigt die gewünschte Funktion" = "matcht" = "frisst (etwas)"

Einfache Ausdrücke

Ein-Elementige reguläre Ausdrücke

Fangen wir mal klein an. Wir wollen prüfen, ob eine Eingabe einer Schulnote von 1-6 entspricht. erledigt das für uns. Du siehst: in eckigen Klammern folgt eine Auflistung von Zeichen, die erlaubt sind. Insgesamt steht der gesamte geklammerte Ausdruck aber nur für ein Zeichen: 1 oder 2 oder ... oder 6.

Da unsere Zahlen von 1 bis 6 so schön aufeinanderfolgen, können wir auch schreiben was die Sache etwas übersichtlicher macht. Genau so könnte man z.b. prüfen, ob eine Eingabe einem Gleis auf einem Bahnhof entspricht: für einen Bahnhof mit 9 Gleisen. Auf unserem Bahnhof sei heute Gleis 4 gesperrt, also als Eingabe nicht erlaubt: Wir haben den Eingabebereich also aufgeteilt in zwei Bereiche 1-3 und 5-9. Du siehst: Die beiden Bereiche werden einfach hintereinander geschrieben. Das ist am Anfang gewöhnungsbedürftig (intuitiv möchte man vielleicht ein Leerzeichen machen), aber das wird uns noch öfter begegnen.

Mehrelementige reguläre Ausdrücke

Was ist, wenn auf unserem Bahnhof jetzt angebaut wird? Sagen wir, er wird auf 12 Gleise erweitert: (Gleis 4 sei wieder geöffnet ;-) ). Aber Vorsicht, hier ist ein Fehler drin!

Wie oben erwähnt, steht der gesamte Ausdruck in den eckigen Klammern nur für ein Zeichen. "12" enthält aber zwei Zeichen. Der Code oben wird nicht funktionieren: Er bedeutet "1 bis 1" oder "2". Den Umgang mit Bahnhöfen mit mehr als 9 Gleisen demonstrieren wir später.

Für den Moment wollen wir etwas anderes Prüfen: Bahnsteige 1-9 haben noch jeweils Abschnitt "a" und "b". Wir wollen also auf 1a,1b,2a,...,9a,9b prüfen: Will heißen: Eine Zahl 1-9 gefolgt von einem Buchstaben a oder b.

Mehrere rechteckige Klammern entsprechen also mehreren Zeichen. Soll ein Ausdruck mehrere Zeichen beschreiben, werden diese einfach hintereinander gehangen. Dann wird die Eingabe von links nach rechts mit deinem Ausdruck verglichen.

Selbstverständlich müssen auch nicht immer alle Buchstaben aufgeführt werden: trifft jetzt auch z.B. 4d.

Zu beachten ist, dass Groß- und Kleinschreibung getrennt behandelt wird. Eine sinnvolle Erweiterung wäre also z.B.

Optionen

Schauen wir uns die Eingabe einer Hausnummer an. Diese können aus einer oder mehreren (bis zu 3) Ziffern bestehen und ein a-z am Ende haben. Müssen aber nicht. Das Fragezeichen hinter einem Element (und [1-9] ist ein Element!) besagt: Das vorhergehende Element kann vorkommen, muss aber nicht.

Der letzte Code heißt also: Eine Ziffer (1-9), optional zwei weitere Ziffern (0-9), optional ein Buchstabe.

Stellt man sich solch eine Konstruktion mit z.B. 10 Ziffern vor, kann man sich leicht ausmalen, dass sie schnell sehr lang wird. Daher gibt es eine andere Schreibweise, wenn man ein Element mehr als ein mal erlauben will: Trifft auf "ah" genau so wie auf "aaah". Die Angabe in Klammern steht also für die {minimale, maximale} Anzahl an Zeichen. Daher können wir unsere Hausnummern auch so formulieren: und dann auch ohne Probleme für längere Straßen (USA ;-) ) umschreiben: erlaubt jetzt auch Hausnummern im fünfstelligen Bereich, fordert aber mindestens eine zweistellige (beachte die Änderung vor dem Komma).

Eine Konstruktion wie (also eine Klammer mit nur einer Angabe) verlangt eine genau x-malige Wiederholung des davorstehenden Ausdrucks. In unserem Fall 5 mal: Dieser reguläre Ausdruck wäre geeignet, um (deutsche) Postleitzahlen zu prüfen.

Wir könne auch ein "mindestens" ausdrücken: fordert mindestens 3 Ziffern.

Beliebige Wiederholungen

Bisher mussten wir immer wissen, wieviele Zeichen gefunden werden sollen. Es gibt aber durchaus Fälle, in denen sich ein Zeichen beliebig oft wiederholen darf.

Schauen wir uns die Prüfung von Telefonnummern an. Wenn man einen regulären Ausdruck schreibt, empfiehlt es sich oft, zunächst einmal zu notieren, auf was dieser alles treffen soll. Unsere Telefonnummern sollen von diesem Format sein dürfen:

Es können neben Zahlen also auch Bindestriche, Querstriche, Leerzeichen und Punkte vorkommen. Ein gültiges Element wäre also (Also: Dieses eine Zeichen besteht aus einer Zahl zwischen 0 und 9 oder einem Slash oder einem Punkt oder einem Leerzeichen oder einem Minus.) Doch Vorsicht! Wie du siehst, kommen in den Klammern zwei Bindestriche vor: ein mal in einer speziellen Funktion, um einen Zahlenbereich anzudeuten und ein mal als "echter" Bindestrich, der so auftauchen darf. Um Verwechslungen auszuschließen müssen wir letzteren maskieren, also zeigen dass er hier keine spezielle Funktion hat. Das geht mit einem vorangestellten Backslash: Und dann wollen wir ja noch mitteilen, dass diese Zeichen beliebig oft vorkommen dürfen: Das Plus bedeutet: ein oder mehrmals das davor befindliche Zeichen, also mindestens ein mal. Wollen wir auch eine leere Telefonnummer zulassen (der Mathematiker würde sagen: Die Nullnummer), so gibt es dazu ein anderes Zeichen: trifft jetzt auch auf "" (leere Zeichenkette) und natürlich unsere Telefonnummern.

Dieser Ausdruck ist jetzt natürlich ein sehr allgemeiner Ausdruck (siehe die Diskussion oben). Er erlaubt auch Telefonnummern, die offensichtlich nicht korrekt sind, wie --..//123 Möchte man das weiter einschränken, muss man einen komplizierteren Ausdruck entwickeln.

Also nochmal zusammengefasst: Das + steht für eine mindestens einmalige Wiederholung. * heißt: keinmal oder beliebig oft.

Platzhalter

Gehen wir zu einem anderen Beispiel über: Wir möchten in einer Bibliothek nach einem Autor suchen, wissen aber nicht, ob er evtl. einen zweiten Vornamen hat. Nach dem Vornamen können also beliebige Zeichen kommen und danach dann erst der Nachname. Eine mögliche Lösung sieht so aus: Marius .*Osterhase Die im Einzelnen bedeutet: "Marius", danach ein Leerzeichen gefolgt von einem beliebigen Zeichen (dafür steht der Punkt!) beliebig oft (also evtl. auch gar keins) und anschließend der Nachname. Das trifft auf "Marius Osterhase" genau so wie auf "Marius Müller Osterhase".

Genau so kann man dem Punkt natürlich auch ein Plus folgen lassen, um mindestens ein beliebiges Zeichen zu fordern.

Der Punkt "frisst" normalerweise zwar fast alles, aber keine Zeilenumbrüche. Wie du ihn dazu bringst, findest du unter Modifikatoren.

Dem "aufmerksamen Leser" wird nicht entgangen sein, dass wir oben auch schon einen Punkt hatten, und zwar innerhalb der eckigen Klammern. Wie bei vielen, aber nicht allen Sonderzeichen müssen diese zwar außerhalb von eckigen Klammern als solche maskiert werden (Backslash davor), aber nicht innerhalb.

Zeichenklassen negieren

Angenommen wir wüssten den zweiten Vornamen des Autors nicht genau, aber können uns daran erinnern, dass er kein q und kein z enthält. Auch kein Problem: verlangt nach einem zweiten Vornamen (deswegen das Plus) und lässt dazu ein beliebiges Zeichen zu, das nicht q oder z ist. Das ^ negiert also eine Zeichenklasse und gilt genau bis zur schließenden Klammer.

Klammern

Klammern können dazu benutzt werden, längere Ausdrücke zu einem Element zusammenzufassen und es damit zu ermöglichen, das oben gelernte auch auf Teilausdrücke anzuwenden: Marius (Müller )?Osterhase trifft genau auf "Marius Müller Osterhase" oder "Marius Osterhase" und auf nichts anderes: Das Fragezeichen bezieht sich - dank der Klammern - auf den kompletten zweiten Vornamen und das folgende Leerzeichen.

Ebenso lässt sich schreiben: Was jetzt sowohl für dieses gelbe Ding als auch z.B. für "Banananane" steht. Auch hier lässt sich die Notation mit den geschweiften Klammern anwenden: was jetzt natürlich eine entsprechend andere Bedeutung hat.

Alternativen

Mit Klammern kann man aber auch noch andere Sachen machen, z.B. Alternativen zu einem Teilausdruck angeben: Das Wetter ist (toll|richtig schlecht) In diesem Beispiel dürfen als letzte Worte "toll" oder "richtig schlecht" vorkommen, aber nicht beide.

Modifikatoren

In allen RegEx-Varianten kannst du sogenannte Modifikatoren setzen und damit das genaue Verhalten des Ausdrucks kontrollieren. In Java kannst du dies z.B. bei der Konstruktion eines matcher-Objektes erledigen, bei PHP hat ein regulärer Ausdruck immer die Syntax [Begrenzungszeichen][RegEx][Begrenzungszeichen][Modifikator(en)] also z.B. /(Mein|Ausdruck)/im Dabei sind die Slashes die Begrenzungszeichen (andere sind hier denkbar, z.B. ~), und "i" und "m" in diesem Fall Modifikatoren. Gängige Modifikatoren sind unter anderem:

i Case-Insensitivity (die Nichtbeachtung von Groß- und Kleinschreibung) einschalten

s Punkt wird multilinefähig: Der Punkt frisst auch Zeilenumbrüche, dies ist standardmäßig nicht so.

m Zeilenmodus: Die Zeichen ^ und $ matchen auch auf Zeilenanfänge bzw. -enden. Ohne den Modifikator passen sie nur auf Anfang und Ende der gesamten Zeichenkette.

Modifikatoren beziehen sich immer auf den ganzen Ausdruck und sind daher eine leicht übersehene Fehlerquelle.

Kompliziertere Ausdrücke

Verschachtelungen

Selbstverständlich dürfen auch diverse Klammern ineinander geschachtelt werden, wie z.B. in folgendem Ausdruck: (VW (Golf|Polo)|Fiat (Punto|Panda)) welcher auf "VW Golf", "VW Polo", "Fiat Punto" und "Fiat Panda" trifft. Dies ermöglich zwar recht kurze Ausdrücke für lange Zeichenketten, kann aber auch viel Zeit zur Verarbeitung in Anspruch nehmen.

Genau so lassen sich auch diese Alternativen wiederholen: Beschreibt eine Folge aus Nullen und Einsen, in der maximal 2 Nullen oder Einsen aufeinander folgen. (Falls das jetzt nicht direkt klar wird: einfach mal überlegen, was man aus "10" und "01" zusammensetzen kann.)

Gierige Ausdrücke

Ein Beispiel aus der Praxis: Der folgende, etwas längerer Code soll uns aus einer HTML-Datei die Links bzw. deren Zieladressen heraussuchen. Ein Link in einem HTML-Quellcode hat i.d.R. ein Format wie dieses: <a href="[...Zieladresse...]" [...weitere Angaben...]> z.B. <a href="http://www.example.org/" target="_blank"> Ein möglicher Ausdruck dafür ist recht schnell gefunden: Sieht gut aus, klappt aber nicht. Warum?

Der Autor hat sich zwar gedacht der Ausdruck würde an der zu dem Link gehörenden schließenden Klammer aufhören, er tut es aber nicht. Im folgenden ein Ausschnitt aus einer HTML-Datei mit markiertem Treffer: <BODY> Bla Blubb 1 2 3 <a href="http://www.example.org/" target="_blank"> Linktext </a> Viel, viel weiterer <i>Text</i> Blubb 42 Das geht eindeutig zu weit! Der zweite Punkt ist zu "gierig" und frisst alle Zeichen sogar über mehrere schließende spitze Klammern hinweg. In anderen Situationen wäre es denkbar, dass der erste Punkt ähnlich amokläuft.

Für unseren Fall gibt es zwei Lösungsmöglichkeiten. Oft bleibt jedoch nur eine davon, daher stelle ich beide vor:

Den Punkt ersetzen durch etwas was keine spitzen Klammern mehr frisst: Das ist eine oft anzuwendende Methode (überlege dir, welche Zeichen das Ende markieren und schließe diese von dem zu treffenden aus). Das ganze muss jetzt natürlich noch analog für den ersten Punkt gemacht werden:

Den Punkt "umerziehen" (bzw. * genügsam machen) so dass beide zusammen nur noch so viel fressen, wie unbedingt nötig ist. Dies tut ein angehängtes Fragezeichen, was dann natürlich nicht mehr die bisher bekannte Funktion hat: Natürlich funktioniert diese Sonderbenutzung des Fragezeichens auch hinter einem Pluszeichen.

Gruppen

So gut wie alle RegEx-Dialekte erlauben das Bilden von Gruppen und deren Speicherung für spätere Verwendung. Auch dazu können Klammern dienen.

Spätere Verwendung kann z.B. heißen, dass ein Ausdruck zwar auf eine längere Zeichenkette treffen soll, aber nur ein Teil davon wirklich verwendet werden soll.

Soetwas wäre in Verbindung mit dem obigen Ausdruck sehr nützlich, um alle Zieladressen aus einer HTML-Seite zu filtern.

Wie genau du an die Inhalte der Gruppen kommst, steht in der Anleitung der von dir verwendeten Sprache. Bei PHP findest du es z.B. bei preg_match und Java soweit ich mich erinnern kann irgendwo in der Nähe von regex.matcher. Google sollte da mehr zu wissen. Der erste Punkt und seine "Vervielfacher" sind eingeklammert. In der ersten Gruppe befindet sich jetzt also die URL.

Beachte, dass bei der Nummerierung der Gruppen die Reihenfolge der öffnenden Klammer zählt. Das ist wichtig zu beachten bei verschachtelten Klammern. Außerdem zählen normalerweise alle Klammern, auch wenn sie nur zur Ausweisung von Alternativen (s.o.) verwendet werden.

Referenzen

Es gibt noch einen anderen, sehr nützlichen Verwendungszweck für Gruppen. Stell dir vor, du willst in einer Folge von Zahlen (sagen wir sie seien mit einem Leerzeichen voneinander getrennt) all die Zahlen finden, die mit der selben Ziffer beginnen und enden.

Wir haben also 129 337873 78324 43938 9388 824998 349734 Dazu können wir solch einen Ausdruck verwenden: wobei das \1 auf den Inhalt der ersten Klammer verweist (referenziert) und daher an der Stelle von \1 das selbe stehen muss wie in dieser Klammer. Das erste und letzte Zeichen unseres Ausdrucks ist jeweils ein Leerzeichen, um damit das Ende und den Anfang einer Zahl zu kennzeichnen.

Schauen wir uns an, auf was er trifft: 129 337873 78324 43938 9388 824998 349734 Wie du siehst, zählt das erste und letzte Leerzeichen auch immer noch zu dem jeweiligen Treffer dazu. Das ist auch kein Wunder, schließlich steht vor und hinter unserem Ausdruck auch jeweils ein Leerzeichen. Das ist nicht wirklich gut, wenn uns dieses Leerzeichen eigentlich gar nicht interessiert. Daher stellt die RegEx-Sprache auch hier ein Mittel zur Verfügung, welches auch funktioniert, wenn wir keine Gruppen benutzen möchten oder können:

Spezielle Zeichen: Wortgrenzen

Es gibt Zeichen, die stehen zwar im regulären Ausdruck, aber nicht in dem Text, der nachher gematcht wird. Das sagt dir jetzt nichts? Naja, schauen wir uns als ein Beispiel an, wie man obigen Ausdruck ohne die Leerzeichen schreibt: Dieses "\b" ist ein zusammengehöriges Element und kennzeichnet ein Wortanfang oder ein Wortende (also eine Wortgrenze). Kennst du die in Textverarbeitungsprogrammen und Editoren oft verwendete Möglichkeit "Nur ganzes Wort suchen" in der Suchfunktion? Wählst du diese aus, werden z.B. bei einer Suche nach "Kai" statt diesen Treffern nur noch dieser Treffer gefunden: Kai fährt nach
Kaiserslautern Letzteres ließe sich in regulären Ausdrücken so umsetzen bzw. ausdrücken: Also "Kai" nur, wenn es von Wortgrenzen umgeben ist. Was genau ist überhaupt eine Wortgrenze? Eine Wortgrenze tritt zwischen einem Wort-Zeichen und einem Nichtwortzeichen auf. Hä? (Erklärung folgt!)

Spezielle Zeichen: Weitere Zeichen

Es gibt - Überraschung! - noch mehr Sonderzeichen, die du verwenden kannst und die einen RegEx schön abkürzen können. Diese bestehen immer aus einem Backslash gefolgt von einem weiteren Zeichen (Buchstaben). Übrigens steht ein großer Buchstabe dabei immer für das Gegenteil eines kleinen. \w \W Ein Wort-Zeichen (kleines w) steht genau für [a-zA-Z0-9] und ein Nichtwortzeichen (großes W) steht genau für alles andere, also [^a-zA-Z0-9]. \d \D Ein Digit, also eine Ziffer von 0-9. Entspricht damit [0-9] und in der Großschreibung [^0-9]. \b \B Die "kleine" Variante hast du ja oben schon kennengelernt. Ein großes B steht dementsprechend für alle Stellen, an denen keine Wortgrenze auftritt. \s \S Die Kleine Variante steht für alle Whitespaces: Das sind so gut wie alle Zeichen, die man nicht sieht. Also Return (bzw. Enter), Leertaste (Space), Tab(ulator). \\ Wenn man mit Backslash, wie du gesehen hast, Sonderzeichen anfängt, muss man ja auch den Backslash selbst irgendwie fabrizieren können, wenn man genau diesen meint. Dies macht man einfach durch einen doppelten solchen. Man sagt: der erste "maskiert" den zweiten. \. \+ \* \( \) \[ \] \- \$ \| Der Punkt und viele andere Zeichen haben, wie du oben gesehen hast, eine Sonderfunktion. Daher werden sie mittels eines Backslashs von dieser Sonderfunktion abgehalten ("maskiert"). Dies gilt für das Minuszeichen nur innerhalb von Zeichenklassen, dafür darfst du in selbigen bei vielen anderen Zeichen die Backslashs weglassen.

Anfangs- und Endezeichen

Stell dir vor, du willst ein Datum prüfen, sagen wir mit 1\.3\.2004 Und diesen Ausdruck lässt du auf 11.3.2004 los. Was kommt raus? Dein Ausdruck wird dir melden, dass er trifft. Ist ja auch irgendwo klar, schließlich steht, wenn man eine Eins weglässt, dein Datum da. Du willst aber, dass das gesamte Datum wie deins aussieht? Gibt's da nicht was von Ratiodingsbums? Doch gibts: Dieses Dach am Anfang sagt: Treffe nur, wenn hier der zu durchsuchende String anfängt. Und das Dollarzeichen steht für: Treffe nur, wenn genau hier der zu durchsuchende String endet.

Selbstverständlich kann man beide auch einzeln einsetzen; Als Beispiel schauen wir uns zwei reguläre Ausdrücke an: Der erste trifft auf einen String, der mit einer Zahl endet. Der zweite trifft auf einen String, der mit einer öffnenden Klammer beginnt. Dabei benutzen wir die oben angegebene Maskierung für die Klammer.

Positive Lookaheads und Lookbehinds

Du hast jetzt Möglichkeiten kennengelernt, wie du Wortgrenzen feststellen kannst, ohne sie selbst wirklich mitzufressen. Es gibt auch eine universelle Möglichkeit.

Als Beispiel soll folgende Zahlenfolge dienen: 000566403580000345050052301078906040092800100001007680 Aus dieser Zahlenfolge sollen alle jene Folgen herausgefischt werden, die drei Ziffern ungleich Null enthalten und von einer Null an jeder Seite begrenzt werden. In unserem Fall also "358", "345", "523", "789", "928" und "768". Dazu kannst du folgenden Code benutzen: Keine Panik! Zur Erklärung, und wir fangen in der Mitte an: das [1-9]{3} sollte klar sein (3 Ziffern ungleich Null). Davor findest du die Klammer (?<=0). Die Zeichen ?<= musst du "en bloc" betrachten und sie kennzeichnen eine Sonderfunktion für die Klammer. Diese steht damit für "vornedran eine Null". Und weil "vornedran" in der normalen Leserichtung der westlichen Welt bedeutet, dass die Klammer "rückwärts" schaut, heißt diese Funktion "positive lookbehind".

Genauso mit den Zeichen "?=", diese weisen der Klammer die Sonderfunktion "hintendran muss ... kommen" zu, in unserem Fall also "hintendran eine Null". Das ganze nennt sich "positive lookahead", weil die Klammer "nach vorne" schaut.

Unser "\b"-Beispiel von oben könnten wir also auch folgendermaßen ausdrücken, wenn wir unser Wissen über "\W" benutzen: Zur Erklärung fangen wir wieder in der Mitte an: Da steht der von oben bekannte Code (Eine Ziffer, beliebig viele weitere Ziffern und eine weitere Ziffer, die die gleiche wie die erste ist). Die allererste Klammer fordert vor der ersten Ziffer ein Nichtwortzeichen und die allerletzte Klammer ein Nichtwortzeichen hinter der letzten Ziffer.

Selbstverständlich können lookaheads und lookbehinds auch einzeln in einem regulären Ausdruck vorkommen und sogar mehrfach. Das einzige, was nicht geht, ist ein lookbehind mit einer unbekannten Anzahl von Zeichen. (also so Dingern wie *, + und {0,4})

Alles klar? Wenn nicht, dann zurück zum Anfang dieses Abschnitts, gehe nicht über "Los" und ziehe keine 200 RegExen ein.

Negative Lookaheads und Lookbehinds

Du hast das kompliziert gefunden und siehst so langsam ein, wie voodoo reguläre Ausdrücke sind? Es kommt noch besser.

Dazu noch ein Beispiel: Du willst eine E-Mail-Adresse prüfen, die aber nicht aus Frankreich stammen soll. Zunächst schauen wir uns eine einfache E-Mail-Adressen-Prüfung ohne die Einschränkung an: [^@]+@.+\.[^.]+ (Ein Zeichen ungleich dem @-Zeichen, gefolgt von einem @-Zeichen, beliebigen Zeichen (aber mindestens eins), einem Punkt und dahinter beliebige Zeichen, aber kein Punkt mehr. Trifft auf toll@example.com genau so wie auf viele_.Zeichen@noch.mehr.Punkte.example.com.) Wichtig: das letzte Paar von eckigen Klammern frisst immer den letzten Teil (die Top-Level-Domain, im Beispiel "com") der E-Mail-Adresse.

Jetzt fügen wir eine Beschränkung ein und wollen "fr" an dieser Stelle ausschließen. Auf den Punkt darf also nicht "fr" folgen. erledigt das für uns. ?! kennzeichnet eine Klammer also als "hier darf nicht folgen".

Damit könnte man auch eine besondere Suche nach einem Wort gestalten. trifft auf alle Wörter, die mit "F" anfangen, aber ungleich "Feta" sind. Im folgenden Text sind alle Strings markiert, die getroffen werden: Das war der "negative lookahead".

Und weils so schön war, leg ich noch einen drauf, den "negative lookbehind": trifft auf alle Eimer, in die kein Müll gehört, meint also: "treffe an dieser Stelle, wenn davor kein "Müll" steht".

Ich hoffe, dieses kleine Tutorial hat dir etwas durch den Zeichendschnungel der Regulären Ausdrücke geholfen. Falls jetzt noch Fragen offen sind, empfehle ich die oben genannten Websites als Nachschlagewerke. Wenn du das Tutorial hilfreich fandest, würde ich mich auch über eine Spende freuen:

# data[].attributes.attachment_link.description
"Reguläre Ausdrücke" sind eine Art Sprache, die beim Programmieren für diverse Problemlösungen verwendet werden kann, insbesondere dann, wenn es darum geht, Zeichenketten (Strings) zu bearbeiten, zu prüfen oder in ihnen etwas zu suchen. Und weil der Name "Reguläre Ausdrücke" etwas unhandlich ist, heißen die "Regular Expressions" auch oft einfach nur "RegEx(en)".

# data[].attributes.attachment_link.readable_text
1. Rufen Sie diese Webseite auf: https://www.weltfussball.de/alle_spiele/bundesliga-2019-2020/

2. Speichern Sie den HTML-Code der Seite lokal auf Ihrer Festplatte (Chrome: Speichern als html-Seite; Safari: Quelltext speichern; Firefox: nur HTML speichern)

3. Öffnen Sie den HTML-Code in Ihrem Texteditor (Sublime) und formulieren einen Regulären Ausdruck für die URLs, die zu den Spielberichten führen. Gehen Sie dazu auf der Originalseite auf die Links hinter den Spielergebnissen. Wenn Sie diese aufrufen, sehen sie z.B. in der Browserzeile, wie sie aufgebaut sind. Achtung: im Quellcode ist die Domain selbst (also weltfussball.de) ausgelassen.

4. Markieren Sie über den Find all-Befehl alle URLs, kopieren Sie sie (ctrl-C) und fügen Sie sie in einem neuen txt-File ein.

5. Ergänzen Sie nun noch die Domain https://www.weltfussball.de am Anfang und tilgen Sie ggf. den zusätzlichen Text am Ende. Tipp: Mit ^ können Sie den Zeilenanfang suchen und einfach durch den einzufügenden Text ersetzen.

6. Am Ende sollten Sie eine Liste mit 306 URLs zu allen Spielen der Bundesligasaison 2019/20 haben.

# data[].attributes.attachment_link.readable_text
Aufgabe I: Generieren Sie aus den URLs Metadaten in dieser Form:

<text source="kicker" league="bundesliga" season="2020_21" round="8" team1="fc-bayern-muenchen" team2="werder-bremen" url="https://www.kicker.de/bundesliga/2020_21/8/4667120/analyse/fc-bayern-muenchen/werder-bremen">

Aufgabe II: Umschließen Sie die Absätze mit öffnenden und schließenden Tags: <p>Das ist ein Absatz.</p>

-----------

https://www.kicker.de/bundesliga/2020_21/8/4667120/analyse/fc-bayern-muenchen/werder-bremen
Nach sieben Heimniederlagen, die letzten fünf sogar ohne eigenen Treffer, brach Hannover am Freitagabend den Heimfluch: Gegen Mönchengladbach feierte 96 einen verdienten 2:0-Sieg. Dabei gelang dem erst 19-jährigen Waldemar Anton sein erster Bundesliga-Treffer.
Hannovers Trainer Daniel Stendel war nach dem 2:2 bei Hertha BSC zu zwei personellen Veränderungen in seiner Startelf gezwungen: Albornoz und Karaman spielten für die verletzten Bech (Sehneneinriss im Knie) und Prib (muskuläre Probleme). Mönchengladbachs Coach Andre Schubert tauschte ebenfalls zweimal Personal: Im Vergleich zur 0:1-Niederlage beim FC Ingolstadt begannen Raffael und Stranzl für Hahn (Bank) und Xhaka (5. Gelbe).

https://www.kicker.de/bundesliga/2020_21/8/4667121/analyse/bor-moenchengladbach/fc-augsburg
In einer lange Zeit zähen Partie bewies Leverkusen gegen Frankfurt beim 3:0 Effizienz und springt auf Platz drei. Bayer wusste mit seiner Dominanz gegen eine starke Eintracht zunächst wenig anzufangen, erst ein Jokertor brach den Bann und sodann nahm das Schicksal für die Hessen, die deutlich unter Wert geschlagen wurden, seinen Lauf.
Leverkusens Trainer Roger Schmidt tauschte im Vergleich zum 2:0-Sieg im Derby beim 1. FC Köln zweimal Personal aus: Für die gesperrten Ramalho und Wendell rückten Henrichs und Aranguiz (Startelfdebüt) neu ins Team. Kramer wurde zurückgezogen und agierte als Innenverteidiger.

https://www.sportschau.de/bundesliga/2020_21/8/4667122/analyse/tsg-hoffenheim/vfb-stuttgart
In einem phasenweise intensiven Abstiegs-Duell hat der FC Augsburg den VfB Stuttgart mit 1:0 besiegt. Die Führung für die Fuggerstädter war verdient, doch sie verpassten es nachzulegen und mussten so bis zum Schluss um den Dreier zittern. In der Tabelle machte der FCA dadurch einen Sprung, die Gäste hingegen rutschten wieder ganz tief in den Keller.
Augsburgs Trainer Markus Weinzierl wechselte nach dem 2:1-Sieg in Bremen zweimal: Kohr und Koo (nach seiner Gelbsperre) rückten für Janker (Bank) und Esswein (Muskelbündelriss im Oberschenkel) in die erste Elf.

https://www.sportschau.de/dfb-pokal/2020_21/9/4667126/analyse/fc-schalke-04/vfl-wolfsburg
Werder Bremen hat durch einen 3:2-Sieg gegen den VfL Wolfsburg einen sehr wichtigen Sieg im Kampf um den Klassenerhalt eingefahren. Die Grün-Weißen waren über weite Strecken des Spiels die aktivere Mannschaft und zeigten viel Einsatz. Den Wölfen fiel auswärts einmal mehr in der Offensive zu wenig ein.
Bremens Trainer Viktor Skripnik tauschte nach der enttäuschenden 1:2-Heimniederlage gegen den FC Augsburg zweimal: Hoffnungsträger Pizarro kehrte zurück ins Sturmzentrum, für ihn nahm Ujah auf der Bank Platz. Außerdem begann Yatabaré für Öztunali (Bank).

https://www.sportbild.de/champions-league/2020_21/9/4667128/analyse/arminia-bielefeld/bayer-04-leverkusen
Dank des 2:1 gegen Hertha BSC baute die TSG Hoffenheim den Vorsprung auf den direkten Abstiegsplatz 17 auf sieben Punkte aus. Die Berliner gingen in einem Spiel der Standardsituationen in Führung, kassierten jedoch schnell den Ausgleich und bauten in der zweiten Hälfte deutlich ab. Daher fiel der späte Siegtreffer der Kraichgauer durchaus verdient.
Hoffenheims Trainer Julian Nagelsmann nahm nach dem 2:0-Sieg bei Eintracht Frankfurt drei Wechsel in seiner Startelf vor. Statt Rudy (5. Gelbe Karte) sowie Kaderabek und Vargas (beide Bank) begannen Polanski, Amiri und Uth.

# data[].attributes.attachment_link.readable_text
Aufgabe: Formulieren Sie einen Regulären Ausdruck, der alle Zeilen in dieses tabellarische Format (tab-separated file) bringt:
NACHNAME, Vorname	Geburtstag	schulz-detlef@gmail.com

--------

Detlef SCHULZ hat am 21.03.1967 Geburtstag, und das ist seine Emailadresse: schulz-detlef@gmail.net
Ina SCHULTER wurde am 02.04.1992 geboren und ihre E-Mail-Adresse lautet: inaschulter@gmx.de
Katharina Anne LAMAI ist ein Dezemberkind, ihr Geburtstag ist der 08.12.1955 geboren und ihre E-Mail-Adresse lautet: KaAnLa@yahoo.com
Anuk MARGA ist am 15.01.1988 auf die Welt gekommen, ihre E-Mail-Adresse lautet: MargAnuk@gmail.com
Albus Percival Wulfric Brian DUMBLEDORE hat 17.06.1881 das Licht der Welt erblickt. Er hat sogar eine Emailadresse: fresh-dumbledore@hogwarts.com

# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik: Regular Expressions

# data[].attributes.attachment_link.readable_text
Korpus Plenarprotokolle Bundestag

1) Suchen Sie nach einem Personalpronomen gefolgt von einem Modalverb gefolgt von einem Infinitiv!
[pos="PPER"] [pos="VMFIN"] [pos="V.INF"]
2) Finden Sie adverbiale bzw. prädikative Adjektive, die mit klima- beginnen!
[(lemma="klima.*") & (pos="ADJD")]
3) Zählen Sie die häufigsten Possessivkonstruktionen nach dem Muster „unser Land/Demokratie/..." in Reden der SPD-Fraktion aus.
Restricted query → Fraction = SPD → [lemma="unser"][pos="NN"] → unserem Land, unser Land, unserer Gesellschaft
4) In welchem Monat war am meisten vom Brexit die Rede?
[lemma="Brexit"] → Distribution → Based on classification Year_Month (ganz unten) → Januar 2019 (531.85 pmW)
5) Finden Sie Belege für freie Relativsätze in Subjektfunktion!
[(word="W.*") & (pos="PWS")] [pos!="\$\.*"]* "," [pos!="\$\.*"]* [(pos="\$\.") & (lemma!="\?")]
Die Anfrage liest sich so: Ein großgeschriebenes substituierendes Interrogativpronomen, gefolgt von einer beliebigen Anzahl an Nichtsatzzeichen, gefolgt von einem Komma, gefolgt von einer beliebigen Anzahl an Nichtsatzzeichen bis zu einem satzbeendenden Satzzeichen, das aber nicht ein Fragezeichen sein darf.

------------

Korpus Grundsatzprogramme

1) In welchem Programm finden sich die im Schnitt längsten Sätze, in welchem die kürzesten?
[pos="\$\."] → Distribution → Subkorpora Wortanzahl/Subkorpora Satzbeendende Interpunktion: Linke: 19,23 ↔ CSU: 11,7

# data[].attributes.attachment_link.description
1) Suchen Sie nach einem Personalpronomen gefolgt von einem Modalverb gefolgt von einem Infinitiv! [pos="PPER"] [pos="VMFIN"] [pos="V.INF"] 2) Finden Sie adverbiale bzw. prädikative Adjektive, die mit klima- beginnen! [(lemma="klima.*") & (pos="ADJD")] 3) Zählen Sie die häufigsten Possessivkonstruktionen nach dem Muster „unser Land/Demokratie/..." in Reden der SPD-Fraktion aus.

# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik: Das datengeleitete Paradigma I

# data[].attributes.attachment_link.title
Reading Machines – CQPweb / Hypothesen und Operationalisierung

# data[].attributes.attachment_link.title
Reading Machines – Einführung in die Korpuslinguistik

# data[].attributes.body
Lexikografie, Wortprofil, Ausgewogenes Korpus

# data[].attributes.body
Übungen zur Recherche im DeReKo mit COSMAS II

# data[].attributes.attachment_link.title
Reading Machines – Essen gehen I: DeReKo

# data[].attributes.attachment_link.title
Reading Machines – Was ist Korpuslinguistik?

# data[].attributes.body
Keywords, Kollokationen, Ngramme, corpus-based und corpus-driven, Signifikanz, beobachtete und erwartete Werte

# data[].attributes.body
ALI auf CQPweb

# data[].attributes.body
Bubenhofer 2018

# data[].attributes.body
CQP, Hypothese, Operationalisierung, Validität, Reliabilität

# data[].attributes.body
ALI auf CQPweb

# data[].attributes.body
Meier 2017

# data[].attributes.body
DWDS

# data[].attributes.body
Geyken et al. 2017

# data[].attributes.body
KWIC, Kookkurrenzanalyse, Relative Häufigkeiten, Wortformen Operatoren, Trunkierung

# data[].attributes.body
DeReKo (Cosmas II)

# data[].attributes.body
Gredel 2018

# data[].attributes.body
Korpus, Sprachgebrauch, Primärdaten, Metadaten, Annotationen

# data[].attributes.body
Stefanowitsch 2020, Kap. 2