Gorp.NET - eine neue Bibliothek zum Erstellen von Reverse-Vorlagen zum Extrahieren von Daten aus strukturiertem Text

Gorp.NET ist eine neue Bibliothek zum Erstellen umkehrbarer Vorlagen zum Extrahieren von Daten aus strukturiertem Text auf der Grundlage der vorhandenen Salesforce Gorp- Codebasis.

In dieser Veröffentlichung werde ich ein wenig darüber sprechen, wie die Bibliothek zum Analysieren von strukturiertem Text mit dem Namen Gorp verwendet wird (eines der Beispiele für Tools, die manchmal als rückentwickelte Vorlagensysteme bezeichnet werden).
Was ist eine umkehrbare Vorlage im Allgemeinen? Angenommen, wir haben ein bestimmtes System, mit dem wir den benötigten Text auf der Grundlage der von uns ermittelten Anfangsdaten nach strengen Regeln generieren können, die in der Syntax der Vorlagen festgelegt sind. Stellen wir uns nun eine Aufgabe mit umgekehrter Bedeutung vor: Wir haben einen Text mit einer strukturellen Integrität, die durch die Verwendung eines Systems erzielt werden könnte, das auf den Vorlagen aus dem vorherigen Beispiel basiert. Unser Ziel ist es, aus diesem Text die Quelldaten zu extrahieren, auf deren Grundlage sie erstellt wurden. Wenn wir versuchen, eine bestimmte verallgemeinerte Syntax zur Lösung dieses Problems zu finden, die dem entsprechenden Parser bereitgestellt wird, der den Eingabetext in separate Elemente zerlegt, ist dies ein Beispiel für die Syntax zur Implementierung des Konzepts umkehrbarer Vorlagen.

Warum habe ich mich entschieden, speziell über Gorp zu schreiben? Tatsache ist, dass ich mich entschlossen habe, dieses System als Grundlage für die Fertigstellung meines eigenen Projekts zu verwenden - die Geschichte des Projekts selbst, einschließlich einiger Details zu allen Änderungen, die ich am ursprünglichen Gorp- Projekt vorgenommen habe, finden Sie im vorherigen Artikel . Hier konzentrieren wir uns genau auf den technischen Teil, einschließlich der Verwendung einer modifizierten Version des Motors. Der Einfachheit halber werde ich es weiterhin Gorp.NET nennen , obwohl es in Wirklichkeit keine Version von Gorp ist, die nicht auf .NET portiert ist, sondern nur eine leicht polierte und finalisierte Version davon, alle in demselben Java. Eine andere Sache ist, dass das Add-On für die Gorp- Bibliothek selbst (in meiner Version) in Form einer verwalteten DLL namens BIRMA.NET eine eigene spezielle Assembly verwendet - genau das Gorp.NET , das Sie leicht selbst erhalten können, wenn Sie den Quelltext ausführen ( Die Adresse seines Repositorys lautet https://github.com/S-presso/gorp/ ) über das Dienstprogramm IKVM.NET .

Ich werde jetzt bemerken, dass für alle Arten von Aufgaben zum Extrahieren von Daten aus strukturiertem Text die Gorp.NET- Tools selbst für Sie ausreichen werden - zumindest, wenn Sie ein wenig Java beherrschen oder zumindest wissen, wie Sie Methoden aus externen Java-Modulen in Ihren Projekten aufrufen .NET Framework sowie verschiedene Typen aus den dortigen Standard-JVM-Bibliotheken (dies habe ich über das gleiche IKVM.NET erreicht , das jedoch bereits den Status eines nicht unterstützten Projekts hat). Nun, und was werden Sie als nächstes mit den extrahierten Daten tun - dies ist, wie sie sagen, Ihre persönliche Angelegenheit. Gorp und Gorp.NET alleine bieten nur ein nacktes Framework. Einige Grundlagen für die Weiterverarbeitung all dieser Daten enthält das bereits erwähnte BIRMA.NET . Die Beschreibung der BIRMA.NET- Funktionalität an sich ist jedoch bereits ein Thema für eine separate Veröffentlichung (obwohl ich in meinem vorherigen vergleichenden historischen Überblick über BIRMA-Technologien bereits etwas erwähnt habe). Mit Blick auf die Zukunft werde ich mir eine etwas kühne Aussage erlauben, dass die Technologie zur Beschreibung der in Gorp.NET (und dementsprechend in BIRMA.NET ) verwendeten umkehrbaren Vorlagen etwas Einzigartiges unter anderen Handwerken dieser Art ist (ich sage “ Kunsthandwerk “, wie ich es noch nicht gesehen habe, wenn große Unternehmen ihre eigenen Frameworks für diese Zwecke bewarben - nun ja, vielleicht nur Salesforce selbst mit seiner ursprünglichen Implementierung von Gorp ).

Für die vollständige Offenlegung des Konzepts und der technischen Aspekte, die dem in Gorp verwendeten Vorlagenbeschreibungssystem zugrunde liegen, lasse ich hier nur einen Link zur Originaldokumentation in englischer Sprache . Alles, was darin angegeben ist, können Sie sicher in Bezug auf Gorp.NET anwenden . Und jetzt erzähle ich Ihnen etwas über die Essenz.

Die Beschreibung der Vorlage ist also eine Art Textdokument (möglicherweise sogar als einzelne große Zeile dargestellt, die zur Verarbeitung an die entsprechende Methode übergeben werden kann). Es besteht aus drei Teilen, die sequentielle Beschreibungen der drei wichtigsten Elemente enthalten: Muster , Muster und Beispiele (Auszüge).

Der Block der untersten Ebene sind hier Muster - sie können nur aus regulären Ausdrücken und Verweisen auf andere Muster bestehen. Auf der nächsten Hierarchieebene befinden sich Vorlagen , deren Beschreibung auch Verknüpfungen zu benennbaren Mustern sowie Einschlüsse in Form von Textliteralen, Verknüpfungen zu verschachtelten Vorlagen und Extraktoren enthält. Es gibt auch parametrische Muster , auf die ich im Moment nicht eingehen werde (in der Quelldokumentation gibt es nur wenige Beispiele für deren Verwendung). Nun, und schließlich gibt es Beispiele , die bestimmte Syntaxregeln angeben, die benannte Muster bestimmten Vorkommen im Quelltext zuordnen.

Nach meinem Verständnis bestand das ursprüngliche Ziel, das sich die Entwickler von Gorp gesetzt hatten, darin, die in den Berichtsdateien (oder Protokolldateien) enthaltenen Datensequenzen zu analysieren . Betrachten Sie ein einfaches Beispiel für eine bestimmte Anwendung des Systems.

Angenommen, wir haben einen Bericht mit der folgenden Zeile:

<86> 2015-05-12T20: 57: 53.302858 + 00: 00 10.1.11.141 RealSource: "10.10.5.3"


Lassen Sie uns eine Beispielvorlage für das Parsen mit den Gorp- Werkzeugen erstellen :

pattern %phrase \\S+
pattern %num \\d+\n
pattern %ts %phrase
pattern %ip %phrase

extract interm {
template <%num>$eventTimeStamp(%ts) $logAgent(%ip) RealSource: "$logSrcIp(%ip)"
}


Beachten Sie, dass der Vorlagenzuweisungsblock hier sogar weggelassen wird, da alle erforderlichen Vorlagen bereits in der endgültigen Auswahl enthalten sind. Alle hier verwendeten Vorlagen sind benannt, ihr Inhalt wird nach ihrem Namen in Klammern angegeben. Als Ergebnis wird ein Textdatensatz mit den Namen eventTimeStamp , logAgent und logSrcIp erstellt .

Wir werden nun ein einfaches Programm zum Extrahieren der notwendigen Daten schreiben. Angenommen, die von uns erstellte Vorlage ist bereits in einer Datei mit dem Namen extractions.xtr enthalten .
 import com.salesforce.gorp.DefinitionReader; import com.salesforce.gorp.ExtractionResult; import com.salesforce.gorp.Gorp; // ... DefinitionReader r = DefinitionReader.reader(new File("extractions.xtr")); Gorp gorp = r.read(); final String TEST_INPUT = "<86>2015-05-12T20:57:53.302858+00:00 10.1.11.141 RealSource: \"10.10.5.3\""; ExtractionResult result = gorp.extract(TEST_INPUT); if (result == null) { // no match, handle throw new IllegalArgumentException("no match!"); } Map<String,Object> properties = asMap(); // and then use extracted property values 


Ein weiteres Beispiel für eine einfache Parsing-Vorlage:

# Patterns
pattern %num \d+
pattern %hostname [a-zA-Z0-9_\-\.]+
pattern %status \w+

# Templates
@endpoint $srcHost(%hostname): $srcPort(%num)

# Extraction
extract HostDefinition {
template @endpoint $status(%status)
}


Nun, ich denke der Punkt ist klar. Es ist auch nicht falsch zu erwähnen, dass es für die Extraktionsmethode auch eine Definition mit zwei Eingabeparametern gibt, von denen der zweite einen logischen Typ hat. Wenn Sie den Wert auf true setzen , durchläuft die Methode bei der Ausführung alle potenziellen Datensätze, bis sie auf einen geeigneten Wert stößt (Sie können den Methodenaufruf auch durch extractSafe ersetzen - bereits ohne den zweiten Parameter). Der Standardwert ist false , und die Methode kann bei der Diskrepanz zwischen den Eingabedaten und der verwendeten Vorlage "schwören".
Gleichzeitig stelle ich fest, dass Gorp.NET auch eine neue erweiterte Implementierung der Methode extract eingeführt hat : Jetzt gibt es eine Version mit zwei nachfolgenden Parametern eines logischen Typs. Mit einem abgekürzten Aufruf der Sicht extractAllFound setzen wir beide standardmäßig auf true. Der positive Wert des dritten Parameters gibt uns noch mehr Spielraum für Variationen: Ab sofort können wir den Text mit beliebigen Zeicheneinschlüssen in den Intervallen zwischen den gewünschten, bereits strukturierten Stichproben (mit Sätzen extrahierter Daten) analysieren.

Es ist also an der Zeit, die Frage zu beantworten: Was genau kann an dieser Modifikation der Basisversion von Gorp einzigartig sein, abgesehen von der Erweiterung der Extraktionsmethode?
Tatsache ist, dass ich bereits vor einigen Jahren eine Art eigenes Tool zum Extrahieren der erforderlichen Daten aus Text erstellt habe (das auch auf der Verarbeitung bestimmter Vorlagen mit einer eigenen spezifischen Syntax beruhte), das nach etwas anderen Grundsätzen arbeitete. Der Hauptunterschied zu dem in Gorp implementierten Ansatz und allen abgeleiteten Frameworks besteht darin, dass jedes zu extrahierende Textelement einfach durch Auflisten seiner linken und rechten Ränder festgelegt wurde (von denen jedes entweder Teil des Elements selbst sein oder es einfach trennen kann alle nachfolgenden oder vorherigen Texte). Gleichzeitig wurde im allgemeinen Fall die Struktur des Ausgangstextes selbst nicht wie bei Gorp analysiert, sondern nur die notwendigen Teile herausgegriffen. Der Inhalt des Textes, der zwischen ihnen eingeschlossen ist, hätte keiner strukturellen Analyse unterliegen können (es könnte sich durchaus um inkohärente Zeichensätze handeln).

Kann man in Gorp einen ähnlichen Effekt erzielen? In der ursprünglichen Version - vielleicht nicht (korrigieren Sie mich, wenn ich mich irre). Wenn wir einfach einen Ausdruck wie (. *) Schreiben, unmittelbar gefolgt von der Maske, um den linken Rand des nächsten zu durchsuchenden Elements anzugeben, wird mithilfe des Quantifizierers "Habgier" der gesamte nachfolgende Text erfasst. Und wir können in vorhandenen Gorp- Implementierungen keine regulären Regeln mit "nicht gieriger" Syntax verwenden.
Mit Gorp.NET können Sie dieses Problem problemlos umgehen, indem Sie zwei spezielle Mustertypen einführen: (% all_before) und (% all_after) . Die erste ist eine Alternative zur "nicht gierigen" Version (. *) , Die zum Kompilieren eigener Vorlagen geeignet ist. Wie bei (% all_after) wird auch der Quelltext bis zum ersten Auftreten des nächsten Teils des beschriebenen Musters betrachtet, wobei jedoch bereits das Suchergebnis des vorherigen Musters herangezogen wird. Alles, was sich zwischen ihnen befindet, fällt auch in die extrahierbare Teilzeichenfolge des aktuellen Elements. In gewisser Weise "schaut" (% all_after) zurück und "schaut" (% all_before) im Gegenteil nach vorne. Ich stelle fest, dass ein eindeutiges Analogon für (% all_before) in der ersten Version von BIRMA der fehlende linke Rand in der Beschreibung des Elements war und analog zu (% all_after) eine Leerstelle anstelle des rechten Randes war. Wenn beim Beschreiben des nächsten Elements nicht beide Grenzen gesetzt sind, erfasst der Parser offensichtlich den gesamten nachfolgenden Text! All diese damalige Implementierung von BIRMA hat jetzt jedoch eine rein historische Bedeutung (mehr dazu in meinem damaligen Bericht ).
Versteckter Text
Die Quellcodes wurden wegen ihrer extrem geringen Qualität noch nie irgendwo ausgelegt - in Wahrheit könnten sie als Denkmal für das schlechte Design von Softwaresystemen dienen.


Betrachten wir die Funktionen der Verwendung von Dienstmustern (% all_before) und (% all_after) am Beispiel der Aufgabe, bestimmte Benutzerdaten von einer bestimmten Website zu extrahieren. Wir analysieren die Amazon-Website und speziell diese Seite: https://www.amazon.com/B06-Plus-Bluetooth-Receiver-Streaming/product-reviews/B078J3GTRK/ ).
Versteckter Text
Ein Beispiel stammt aus einer Testaufgabe für ein Stellenangebot eines Entwicklers mit einer Spezialisierung auf Datenanalyse, die von meinem Unternehmen gesendet wurde und die leider nicht auf meine vorgeschlagene Lösung des Problems reagiert hat. Richtig, sie haben mich nur gebeten, den allgemeinen Prozess der Lösung zu beschreiben - ohne einen bestimmten Algorithmus anzugeben. Als Reaktion darauf habe ich bereits versucht, auf Gorp-Vorlagen zu verweisen, während meine eigenen Erweiterungen zu dieser Zeit nur "auf Papier" existierten. ".
Aus Gründen der Neugier werde ich mir erlauben, ein Fragment aus meinem Antwortschreiben zu zitieren, bei dem es sich anscheinend um die erste Erwähnung von Gorp.NET handelt , wenn auch privater Natur.
„Um die obige Liste der regulären Ausdrücke, die ich zur Lösung dieses Problems verwende, anschaulicher zu gestalten, habe ich eine vorgefertigte Vorlage zusammengestellt (die dem Brief beigefügt ist), mit der ich alle erforderlichen Daten extrahieren kann, indem ich meine eigene Entwicklung universellerer Natur anwende entwickelt, um diese Art von Problem zu lösen. Der Code basiert auf dem Projekt github.com/salesforce/gorp. Auf derselben Seite finden Sie eine allgemeine Beschreibung der Regeln zum Kompilieren solcher Vorlagen. Im Allgemeinen impliziert eine solche Beschreibung an sich die Zuweisung sowohl konkreter regulärer Ausdrücke als auch der Logik ihrer Verarbeitung. Der schwierigste Punkt hierbei ist, dass wir für jedes Datenmuster die gesamte Struktur des Textes, der sie enthält, und nicht nur die einzelnen Elemente selbst vollständig durch Regulars beschreiben müssen (wie dies beim Schreiben unseres eigenen Programms möglich ist, das nacheinander in einer Schleife wie I sucht) zuvor beschrieben). ”

Die ursprüngliche Aufgabe bestand darin, die folgenden Daten von der obigen Seite zu sammeln:

  • Benutzername
  • Bewertung
  • Titel überprüfen
  • Das Datum
  • Text


Nun, jetzt gebe ich Ihnen nur eine von mir kompilierte Vorlage, mit der Sie diese Aufgabe schnell und effizient erledigen können. Ich denke, die allgemeine Bedeutung sollte ziemlich offensichtlich sein - vielleicht können Sie selbst eine präzisere Lösung anbieten.
Versteckter Text
An diesem Beispiel habe ich im Allgemeinen die Funktionalität meiner eigenen Erweiterungen für Gorp getestet (bereits ohne Beschäftigungsziel, sondern basierend auf der Ideologie des „Proof of Concept“).


pattern %optspace ( *)
pattern %space ( +)

pattern %cap_letter [AZ]
pattern %small_letter [az]
pattern %letter (%cap_letter|%small_letter)
pattern %endofsentence (\.|\?|\!)+
pattern %delim (\.|\?|\!\,|\:|\;)
pattern %delim2 (\(|\)|\'|\")

pattern %word (%letter|\d)+
pattern %ext_word (%delim2)*%word(%delim)*(%delim2)*

pattern %text_phrase %optspace%ext_word(%space%ext_word)+
pattern %skipped_tags <([^>]+)>

pattern %sentence (%text_phrase|%skipped_tags)+(%endofsentence)?

pattern %start <div class=\"a-fixed-right-grid view-point\">

pattern %username_start <div class=\"a-profile-content\"><span class=\"a-profile-name\">
pattern %username [^\s]+
pattern %username_end </span>

pattern %user_mark_start <i data-hook=\"review-star-rating\"([^>]+)><span class=\"a-icon-alt\">
pattern %user_mark [^\s]+
pattern %user_mark_end ([^<]+)</span>

pattern %title_start data-hook=\"review-title\"([^>]+)>(%skipped_tags)*
pattern %title [^<]+
pattern %title_end </span>

pattern %span class <span class=\"[^\"]*\">

pattern %date_start <span data-hook="review-date"([^>]+)>
pattern %date ([^<]+)
pattern %date_end </span>

pattern %content_start <span data-hook=\"review-body\"([^>]+)>(%skipped_tags)*
pattern %content0 (%sentence)+
pattern %content (%all_after)
pattern %content_end </span>

template @extractUsernameStart (%all_before)%username_start
template @extractUsername $username(%username)%username_end
template @extractUserMarkStart (%all_before)%user_mark_start
template @extractUserMark $user_mark(%user_mark)%user_mark_end
template @extractTitleStart (%all_before)%title_start
template @extractTitle $title(%title)%title_end
template @extractDateStart (%all_before)%date_start
template @extractDate $date(%date)%date_end
template @extractContentStart (%all_before)%content_start
template @extractContent $content(%content)%content_end

extract ToCEntry {
template @extractUsernameStart@extractUsername@extractUserMarkStart@extractUserMark@extractTitleStart@extractTitle@extractDateStart@extractDate@extractContentStart@extractContent
}



Das ist wahrscheinlich alles für heute. Zu den von mir implementierten Tools von Drittanbietern, an denen dieses Framework bereits vollständig beteiligt war, darf ich Ihnen ein anderes Mal mitteilen.

Source: https://habr.com/ru/post/de476778/


All Articles