2011-01-08 11 views
24

Gibt es eine Möglichkeit, Regex-Match auf einem Stream in Python zu verwenden? wiePython Regex Parse Stream

reg = re.compile(r'\w+') 
reg.match(StringIO.StringIO('aa aaa aa')) 

Und ich will nicht, dies zu tun, indem Sie den Wert des gesamten Zeichenfolge bekommen. Ich möchte wissen, ob es eine Möglichkeit gibt, Regex in einem Stream (on-the-fly) zu finden.

+0

das ist gegen die Idee von Regex. – SilentGhost

+2

@SlientGhost: Nicht unbedingt. Sie könnten einige (unendliche) Streams mit Regexes parsen, die immer am aktuellen Anfang des Streams übereinstimmen und die Übereinstimmungen als Iterator zurückgeben (und nur die Zeichen konsumieren, die aus dem Stream stammen). – MartinStettner

+0

@MartinStettner: Nun, Sie könnten, wenn es ein automatentheoretischer Matcher ohne Backrefs wäre (und ein paar andere Dinge wie Lookahead-Constraints). Solange der RE auf einen einzelnen endlichen Automaten (entweder NFA oder DFA) kompilieren kann, kann er die Dinge in einem Durchgang abgleichen und damit die Erkennung eines unendlichen Stroms bewältigen. (Aber Python verwendet PCRE, das nicht automatisch-theoretisch ist und alle Bytes früher benötigt.) –

Antwort

15

ich hatte das gleiche Problem. Der erste Gedanke war, eine LazyString Klasse zu implementieren, die wie eine Zeichenkette funktioniert, aber nur so viele Daten aus dem Stream liest, wie aktuell benötigt wird (ich habe das durch __getitem__ und __iter__ reinimplementiert, um Zeichen bis zur höchsten Position abzurufen und zu puffern ...).

Das hat nicht geklappt (ich habe einen "TypeError: erwartete Zeichenfolge oder Puffer" von re.match), also habe ich ein wenig in die Implementierung des re Moduls in der Standardbibliothek geschaut.

Leider scheint die Verwendung von Regexes in einem Stream nicht möglich. Der Kern des Moduls ist in C implementiert und diese Implementierung erwartet, dass der gesamte Eingang auf einmal im Speicher ist (ich denke hauptsächlich aus Gründen der Leistung). Es scheint keinen einfachen Weg zu geben, dies zu beheben.

Ich hatte auch einen Blick auf PYL (Python LEX/YACC), aber ihre Lexer verwendet re intern, so dass dies würde nicht das Problem lösen.

Eine Möglichkeit könnte ANTLR sein, die ein Python-Backend unterstützt. Es konstruiert den Lexer mit reinem Python-Code und scheint in der Lage zu sein, mit Eingabeströmen zu arbeiten. Da für mich das Problem nicht so wichtig ist (ich erwarte nicht, dass mein Input extensiv groß ist ...), werde ich das wahrscheinlich nicht weiter untersuchen, aber es könnte einen Blick wert sein.

+1

Gut recherchiert, interessant. Vielleicht ist http://www.acooke.org/rxpy/ eine sinnvolle Alternative? –

+0

Ich habe gerade eine andere Lösung gefunden: pexpect (http://pexpect.readthedocs.org/en/latest/api/pexpect.html) –

-4

Ja - mit der getvalue Methode:

import cStringIO 
import re 

data = cStringIO.StringIO("some text") 
regex = re.compile(r"\w+") 
regex.match(data.getvalue()) 
+3

Nun, das ist das Gleiche, als würde ich eine Saite füttern, ich frage mich, ob es da ist Jede Möglichkeit, einen Stream zu analysieren – nikitautiu

2

Dies scheint ein altes Problem zu sein. Wie ich auf a similar question gepostet habe, möchten Sie vielleicht die Matcher-Klasse meiner Lösung streamsearch-py unterklassifizieren und Regex-Matching im Puffer durchführen. Überprüfen Sie die Datei "kmp_example.py" für eine Vorlage. Wenn es sich herausstellt, dass der klassische Knuth-Morris-Pratt-Abgleich alles ist, was Sie brauchen, dann würde Ihr Problem mit dieser kleinen Open-Source-Bibliothek gelöst werden :-)

3

Im speziellen Fall einer Datei, wenn Sie Speicher- Ordnen Sie die Datei mit mmap zu und wenn Sie mit Bytestrings anstelle von Unicode arbeiten, können Sie eine Memory-Mapped-Datei an re so füttern, als wäre es ein Bytestring und es wird einfach funktionieren. Dies ist durch Ihren Adressraum begrenzt, nicht durch Ihren Arbeitsspeicher. Ein 64-Bit-Rechner mit 8 GB RAM kann also eine 32-GB-Datei problemlos im Speicher abbilden.

Wenn Sie dies tun können, ist es eine sehr gute Option. Wenn Sie nicht können, müssen Sie sich an unechteren Optionen wenden.


Der 3rd-Party regex Modul (nicht re) bietet teilweise Übereinstimmung Unterstützung, die verwendet werden kann Streaming-Unterstützung zu bauen ... aber es ist chaotisch und hat viele Vorbehalte. Dinge wie Lookbehinds und ^ werden nicht funktionieren, Zero-Width-Matches wäre schwierig, richtig zu bekommen, und ich weiß nicht, ob es richtig mit anderen erweiterten Funktionen regex Angebote interagieren würde und re nicht. Dennoch scheint es einer vollständigen Lösung am nächsten zu sein.

Wenn Sie passieren partial=True zu regex.match, regex.fullmatch, regex.search oder regex.finditer, dann zusätzlich zum vollständigen Übereinstimmungen Berichterstattung, regex auch Dinge berichten, die eine Übereinstimmung, wenn die Daten erweitert werden könnte:

In [10]: regex.search(r'1234', '12', partial=True) 
Out[10]: <regex.Match object; span=(0, 2), match='12', partial=True> 

Es Meldet eine Teilübereinstimmung anstelle einer vollständigen Übereinstimmung, wenn mehr Daten das Übereinstimmungsergebnis ändern könnten, so wird beispielsweise regex.search(r'[\s\S]*', anything, partial=True) immer eine Teilübereinstimmung sein.

Mit diesem können Sie ein gleitendes Fenster von Daten halten, die übereinstimmen, erweitern Sie es, wenn Sie das Ende des Fensters treffen und verbrauchte Daten von Anfang an verwerfen. Unglücklicherweise funktioniert alles, was durch Daten, die vom Anfang der Zeichenfolge verschwinden würden, nicht funktionieren würde, daher sind Lookbehinds, ^, \b und \B out. Zero-width-Matches müssten auch vorsichtig behandelt werden. Hier ist ein Proof of Concept, das ein gleitendes Fenster über einer Datei oder einem dateiähnlichen Objekt verwendet: