Regulärer Ausdruck, der dem doppelten Anführungszeichen entspricht, ohne ein abschließendes Anführungszeichen

687
Rhythem Aggarwal

Ich habe eine große (25 Millionen Zeilen) Datendatei, die durch Pipe ( |) getrennt ist. Der Datenanbieter stellt Dateien bereit, und ich führe automatisierte Jobs aus, um die Dateien in eine Redshift-Datenbank zu laden und dann die Daten zu verarbeiten.

Das folgende ist ein Beispiel der Daten:

123|110092|ACCT|"HC Account"|"Account1||||||||||||"Mary"|||"|"||||132|"STE|504"|1253|Unspecified Account|||N||ACTV|Active||||04/30/2013|12/31/2099||||||||||||| 

Ich habe bis jetzt drei Felddatensätze gesehen:

  1. Ein Textfeld wird in doppelten Anführungszeichen (eingeschlossen ") Zum Beispiel: "HC Account", "Mary", und "|". Dies ist korrekt und die Daten sollten ohne Anführungszeichen geladen werden.
  2. Einige Werte enthalten den Pipe-Begrenzer. Zum Beispiel: "STE|504". In diesem Fall muss das Feld notwendigerweise in Anführungszeichen stehen. Wenn dies nicht der Fall ist, fällt es in die dritte Kategorie.
  3. Manchmal wird nur ein Startzitat bereitgestellt und es gibt kein Endzitat. Zum Beispiel: "Account1.

TL; DR: Jedes Feld, das mit beginnt |", muss mit einem "|. Wenn dies nicht der Fall ist und ein anderes gefunden |"wird, muss das erste doppelte Anführungszeichen mit Escapezeichen versehen werden.

Daher sollte meine Datenzeile folgendermaßen bearbeitet werden, nachdem ich sie in Unix / Python / andere Vorschläge vorverarbeitet habe:

123|110092|ACCT|"HC Account"|"Account1||||||||||||"Mary"|||"|"||||132|"STE|504"|1253|Unspecified Account|||N||ACTV|Active||||04/30/2013|12/31/2099||||||||||||| 


Ich plane, ein Unix-Skript zu schreiben, um die Datei mit SED zu ändern. Der reguläre Ausdruck, den ich bisher geschrieben habe, lautet:

(\|")(?!([a-zA-Z0-9]|\s|\||\/)*("\|)) 

Dies stimmt jedoch nicht mit der Zeichenfolge überein.

Hier ist ein Link, wo ich das teste: https://regexr.com/3toib

Ich möchte den Code leicht halten, da eine durchschnittliche Datei zwischen 3-5 GB groß ist und normalerweise mehrere (10+) solcher Dateien vorhanden sind.

PS Redshift ist ein AWS-Datenbankdienst, der die Postgre SQL Engine verwendet, und kann Anführungszeichen aus ordnungsgemäß zitierten Feldern entfernen und die spezielle Bedeutung eines Zitats mit umgehen \.

Ich bin auch bereit, dies in Python / jeder anderen Skriptsprache zu tun, wenn der Code leicht ist.

0
Woher wissen Sie, ob "|" Ist eine Pfeife in Anführungszeichen oder zwei Startzitate, deren Endzitate fehlen? djsmiley2k vor 5 Jahren 1
in diesem Fall ist das Feld wie Scheiße `|" | "|`. Also | "und" | sind das Rohr innen für das Feld. Aber ich verstehe, dass mit mehr zufälligen Daten dies komplexer werden könnte. Rhythem Aggarwal vor 5 Jahren 0

1 Antwort auf die Frage

1
robinCTS

Es gibt ein RIESIGES Problem mit den Angaben, die Sie für die Daten angegeben haben. Wenn "|"eine gültige Zeichenfolge ist, oder genauer gesagt, wird eine Zeichenkette in Anführungszeichen erlaubt mit einem Rohr zu starten, dann, wenn eine Zeichenfolge mit einem fehlenden Zitat Ende, zum Beispiel "Account1, hat als erstes folgendes einer zitierte Feld, das mit einem Rohr beginnt, zum Beispiel "|Mary", dann Es gibt keine Möglichkeit, in allen Fällen festzustellen, ob das "|Endquot |"Account1||||||||||||"|oder das Startquot für ist |"|Mary"|.

Verwenden Sie zum Beispiel eine (aus Gründen der Lesbarkeit) gekürzte, leicht modifizierte Version der Daten, bei der alle zitierten Zeichenfolgen ab dem zweiten Punkt mit einer Pipe beginnen und die Endanführungszeichen fehlen

123|110092|ACCT|"HC Account"|"Account1||||||||||||"|Mary|||"|||||132|"|STE|504|1253

Es ist ersichtlich, dass dies falsch interpretiert wird

123 110092 ACCT "HC Account" "Account1||||||||||||" Mary   "|||||132|" STE 504 1253

Beachten Sie, dass dies ein Problem ist, wenn Sie Regex, Python oder eine andere Sprache verwenden. Das allgemeine Fallproblem kann "gelöst" werden, aber es wird kompliziert sein und erfordert Kenntnisse über die Anzahl der Felder pro Zeile und die Datenstruktur dieser Felder. (Und es kann immer Randfälle geben, für die nicht gesorgt wird.)


Eine Regex-Lösung, die zumindest die meisten Fälle eines öffnenden doppelten Anführungszeichens erkennt, erfordert jedoch einen Ansatz mit mehreren Durchgängen, da der reguläre Ausdruck den gesamten Text vom Beginn jeder Zeile bis zur ersten unverarbeiteten, unverarbeiteten Öffnung erfassen muss Zitat. (Andernfalls werden, wie Ihr Regex zeigt, selbst in den einfachsten Fällen falsch positive Ergebnisse gefunden.)

Die erforderliche Anzahl von Durchläufen ist die maximale Anzahl von Feldern, die nur in Anführungszeichen eingeschlossen sind, für eine Zeile in der gesamten Datei, plus eins. Das Beenden der Verarbeitung jeder Datei erfordert das Erkennen, wann der Regex keine weiteren Änderungen an der Datei vornimmt.

Dies ist der einfachste reguläre Ausdruck, der in den meisten Fällen funktionieren wird:

 Capturing Group 1 Capturing Group 2 (All previous valid fields) (Unclosed opening quote) __________________________|_________________________ | | || | ^((?:(?:(?!")[^|\r\n]*|"[^"\r\n]*"(?=$|\|))(?:$|\|))*+)(") |____________| |_________________| |______| | | | Unquoted field OR Quoted field EOL or hypen delimiter 

Verwenden Sie es mit dieser Ersatzzeichenfolge:

$1\\$2 

Demo

Wenn der Ersetzungsstring die Anführungszeichen nur zum Öffnen freigibt, so dass das erste Zeichen des verarbeiteten Feldes nicht mehr ein Zitat ist, überspringt der Regex das Feld in nachfolgenden Durchläufen.


Beachten Sie, dass dieser reguläre Ausdruck leider nur in Anführungszeichen angegebene Felder ignoriert, wenn das folgende Feld in Anführungszeichen mit einer Pipe beginnt. Wenn außerdem das nächste angezeigte Feld mit einer Pipe endet, wird auch ein falsches Positiv für ein weiteres nachfolgendes Anführungszeichen generiert.

Als Nebeneffekt der Einfachheit ignoriert der Regex auch Anführungszeichen, die in der Mitte eines Feldes erscheinen. (Dies kann ein Problem sein oder nicht.)


Der Regex kann so verbessert werden, dass er auch dann funktioniert, wenn das nächste folgende Feld mit einer Pipe beginnt:

^((?:(?:(?!")[^|\r\n]*|"[^"\r\n]*"(?:(?=$)|(?=\|)(?!(?:\|[^|"\r\n]*)+[^|\r\n]")))(?:$|\|))*+)(") |____________________________________________| | Modified lookahead to make sure that the following | is not the first char of a properly quoted field 

Demo

Es ist jedoch nicht möglich, das Problem so zu korrigieren, dass es für den Fall funktioniert, in dem das nächste angeführte Feld mit einer Pipe beginnt und endet.

Perfekte Antwort. Danke, dass Sie sich Zeit nehmen und es erklären, als wäre ich 5. Wirklich dankbar. Rhythem Aggarwal vor 5 Jahren 0