Ist das mit regulären ausdrücken lösbar?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 5. Februar 2007, 21:00

Häng an einem Problem fest.

http://docutils.sourceforge.net/docs/us ... ral-blocks

1. Vor einem :: dürfen Blanks, Tabs und auch Zeichen sein. Dahinter das gleiche bis auf Zeichen.
2. Danach muss eine Leerzeile folgen.
3. Es muss danach mindestens eine Zeile folgen die mit mindestens einem Leerzeichen eingerückt sein.
4. Falls danach mehrere Zeilen folgen, müssen die die gleiche Einrückgunstiefe haben wie die Zeile die in Punkt 3 beschrieben ist.
5. Als Abschluss muss eine Leerzeile folgen.


Was ich bis jetzt zusammengebastelt habe ist diese "Fehlerhaft" RegExp.

Code: Alles auswählen

([a-zA-Z0-9 \t]*)(::)\s*\n\s*\n(.*)(\n)
Das (\n) am ende greift zu früh :(

Punkt 3 und 4 geht logischerweise nicht mit RegExp, sondern müsste vom Lexer überprüft werden bzw. vom Parser ;) Soweit ich weiß kann man keine Einrückungen überprüfen mit RegExp.

Beispiele:

Code: Alles auswählen

source = """test
    foobar ::
    
    test
    test2

    """
    
TAGS_RE = re.compile(r'([a-zA-Z0-9 \t]*)(::)\s*\n\s*\n(.*)(\n)')
tokens = [x for x in TAGS_RE.split(source) if x != '' and x != None]
print tokens

# ['test\n', '    foobar ', '::', '    test', '\n', '    test2\n    \n    ']
Hier sieht man das '\n' zu früh greift. Eigentlich muss erst solange (.*) abgearbeitet werden und dann erst (\n).
Also es müsste dan so aussehen:

Code: Alles auswählen

['test\n', '    foobar ', '::', '    test\n', '    test2\n    ', '\n', '    ']
Ist das überhaupt zu Lösen mit RegExp?

Weil bisher geht es bis auf das mit den (\n). -> Beweis:

Code: Alles auswählen

source = """test
    foobar ::
    test
    test2
    """
# Greift nicht, also i.O. -> ['test\n    foobar ::\n    test\n    test2\n    \n    ']


source = """test
    ::
    
    test
    test2
    """
# Greift also i.O. -> ['test\n', '    ', '::', '    test', '\n', '    test2\n    ']

source = """test
    foobar ::
    
    test
    test2

    """
# Greift also i.O. -> ['test\n', '    foobar ', '::', '    test', '\n', '    test2\n    \n    ']
Ansich funktioniert es also schon recht gut, bis auf...

Kann mir einer vielleicht sagen wo ich den Denkfehler gemacht habe?

Ich hoffe der Post ist verständlich für Die jenigen die sich mit dem Thema auskennen.

lg
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Montag 5. Februar 2007, 21:03

Warum willst du reST lexen?
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 5. Februar 2007, 21:19

birkenfeld hat geschrieben:Warum willst du reST lexen?
Deshalb: http://www.python-forum.de/post-57578.html#57578
sape hat geschrieben:[...]Meine wird wohl ein wenig Overkill, da ich gleich einen allgemeinen Markup=>AST=>X-Beliebiges Format-Generator schrieben werde (Bin schon ziemlich weit.), der auch BBC, reStr, etc in ein AST überführen kann. -- Also für euer vorhaben wird es wider zu groß denke ich.
[...]
Aber warum die Frage? Ist die Idee nicht gut?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Montag 5. Februar 2007, 21:23

Weil es
a) sehr komplex ist
und
b) die docutils genau das gleich schon machen (siehe docs, Stichwort "document tree").
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 5. Februar 2007, 22:05

birkenfeld hat geschrieben:Weil es
a) sehr komplex ist
Man kann aber viel lernen.
birkenfeld hat geschrieben: b) die docutils genau das gleich schon machen (siehe docs, Stichwort "document tree").
Dan bin ich aber dafür auf Docutils angewiesen. Das wollte ich eigentlich nicht.


Ich will eigentlich nur wissen wo ich den Fehler oben gemacht habe, da ich schon über eine Stunden an dem re-string size und nicht weiterkomme.

Oder mal anders: Ist es möglich reSTR mit RegExp zu tokenisieren? Mir geht es nicht um Sachen wie Einrückungen feststellen, etc. Das übernimmt ehe nachehr ein Parser bzw. Grammar der den Token-Strom kriegt.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Montag 5. Februar 2007, 22:27

sape hat geschrieben: Oder mal anders: Ist es möglich reSTR mit RegExp zu tokenisieren? Mir geht es nicht um Sachen wie Einrückungen feststellen, etc. Das übernimmt ehe nachehr ein Parser bzw. Grammar der den Token-Strom kriegt.
Ich würde mal auf "nein" tippen. Aber http://docutils.sourceforge.net/ sollte dir das besser beantworten können.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Montag 5. Februar 2007, 22:34

Ok, danke. Werde es mal durcharbeiten.

Aber die Sache oben ist bestimmt zu lösen. Es geht ja soweit bis auf die eine Angesprochene Sache. Aber ob der Rest geht....
Benutzeravatar
Luzandro
User
Beiträge: 87
Registriert: Freitag 21. April 2006, 17:03

Dienstag 6. Februar 2007, 08:54

Unabhängig davon, ob die Sache sinnvoll ist:
sape hat geschrieben:

Code: Alles auswählen

TAGS_RE = re.compile(r'([a-zA-Z0-9 \t]*)(::)\s*\n\s*\n(.*)(\n)')
tokens = [x for x in TAGS_RE.split(source) if x != '' and x != None]
print tokens

# ['test\n', '    foobar ', '::', '    test', '\n', '    test2\n    \n    ']
Hier sieht man das '\n' zu früh greift. Eigentlich muss erst solange (.*) abgearbeitet werden und dann erst (\n).
Also es müsste dan so aussehen:

Code: Alles auswählen

['test\n', '    foobar ', '::', '    test\n', '    test2\n    ', '\n', '    ']
Wieso verwendest du hier eigentl. split? Der erste und der letzte Teil deiner Liste haben somit überhaupt nichts mit deiner Regex zu tun und sind nur der gesplittete Teil mit den einzelnen Gruppen dazwischen. Damit das '\n' nicht zu früh greift, müsstest du 're.DOTALL' aktivieren, damit auch ein Newline bei '.' gematcht wird, allerdings hast du dann natürlich auch alle Zeilen als eine einzelne Gruppe und nicht das Ergebnis, das du wolltest. Das geht aber mit einem einzelnen regulären Ausdruck IMHO sowieso nicht, da du zwar die Zeilen als einzelne Gruppe definieren kannst, die öfter auftritt, nur bekommst du beim Abfragen immer nur die letzte gematchte Gruppe:
If a group is contained in a part of the pattern that matched multiple times, the last match is returned.
[url=http://www.leckse.net/artikel/meta/profilieren]Profilieren im Netz leicht gemacht[/url]
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Dienstag 6. Februar 2007, 21:43

Luzandro hat geschrieben: Wieso verwendest du hier eigentl. split? Der erste und der letzte Teil deiner Liste haben somit überhaupt nichts mit deiner Regex zu tun und sind nur der gesplittete Teil mit den einzelnen Gruppen dazwischen.
Exakt und ist auch so gewollt.
http://www.python-forum.de/post-57598.html#57598

Also ich habe verschiedene RegExp und verwende split auf den ganzen text. Vorfinden tu ich dann einzelne tokens die dann der Lexer überprüft und mit Type zurückgibt. Mehrdeutigkeiten kann der Lexer auch schon auflösen.

Deine Feststellung ist also schon so beabsichtigt.

Mal ein Beispiel:

Code: Alles auswählen

source = source = """test #link[test] **__test__**
foobar
"""
blex = BasisLexer(source)
# So würde das Ergebnis mit Split aussehen: 
# ['test ', '#link', '[', 'test', ']', ' ', '**', '__', 'test', '__', '**', '\n', 'foobar', '\n']
print blex.source

#Der zusammengesetzte RegExp:
# (\*\*)|(//)|(__)|({{{)|(}}})|(#link)([ \t]*)(\[{1})([^\[\].]+)(\]{1})|(#image)([ \t]*)(\[{1})([^\[\].]+)(\]{1})|(\n)|(\r\n)
print blex.regexp_str

#...

#Der fertige lexer_stream
# [[('Literal', 'test ')], [('MarkupCommand', '#link'), ('TOCMD_OPENBR', '['), ('Literal', 'test'), ('TOCMD_CLOSBR', ']')], [('Literal', ' ')], [('MarkupBold', '**'), ('MarkupUnderline', '__'), ('Literal', 'test'), ('MarkupUnderline', '__'), ('MarkupBold', '**')], [('MarkupLinebreak', '\n')], [('Literal', 'foobar')], [('MarkupLinebreak', '\n')]]

# Darüber iteriert
for line in blex:
    print line

# [('Literal', 'test ')]
#[('MarkupCommand', '#link'), ('TOCMD_OPENBR', '['), ('Literal', 'test'), ('TOCMD_CLOSBR', ']')]
#[('Literal', ' ')]
#[('MarkupBold', '**'), ('MarkupUnderline', '__'), ('Literal', 'test'), ('MarkupUnderline', '__'), ('MarkupBold', '**')]
#[('MarkupLinebreak', '\n')]
#[('Literal', 'foobar')]
#[('MarkupLinebreak', '\n')]
Also alles kein Problem :)
Luzandro hat geschrieben:Damit das '\n' nicht zu früh greift, müsstest du 're.DOTALL' aktivieren, damit auch ein Newline bei '.' gematcht wird, [...]
Ich glaube das hatte ich schon probiert und führte auch nicht zum gewünschten Ergebnis. Trotzdem danke für den Tipp. Werde es nochmal testen. Vielleicht habe ich da was übersehen gehabt.

@cracki:
Kodos habe ich schon lägst installiert. Das benutze ich auch ab und an zum testen. Aber das Programm kann mir keine RegExp generieren, das muss man immer noch selber machen. Daher ist das keine Lösung für mein Problem. Wollte eigentlich nur einen Anhaltspunkt wo der Fehler liegt..

@Birkenfeld:
birkenfeld hat geschrieben:
sape hat geschrieben: Oder mal anders: Ist es möglich reSTR mit RegExp zu tokenisieren? Mir geht es nicht um Sachen wie Einrückungen feststellen, etc. Das übernimmt ehe nachehr ein Parser bzw. Grammar der den Token-Strom kriegt.
Ich würde mal auf "nein" tippen. Aber http://docutils.sourceforge.net/ sollte dir das besser beantworten können.
Man kann also reSTR nicht mit RegExp tokenisieren und ist "zu" Komplex?
Finde es interessant wenn man sich mal folgende Source von dir anschaut: http://trac.pocoo.org/browser/pygments/ ... order=name

Ja, stimmt die RegExp macht nicht wirklich was ich gerne möchte:

Code: Alles auswählen

# Code blocks
499	            (r'(::)(\n)((?:(?: +.*|)\n)+)',
500	             bygroups(String.Escape, Text, String)),
501	            include('inline'),
Mal jetzt Spaß bei Seite und mal ernst: Es geht also doch zu tokenisieren. Die Überprüfung der Syntax und Semantik muss nachehr ehe ein Parser übernehmen (Welcher macht das bei euch?), darum geht es aber auch nicht. Es geht erstmal ums triviale Tokenisieren und das geht ja anscheinend doch wie man an deinem Code sehen kann. Die RegExp von dir macht zwar nicht das was ich will, wird aber auf eurer interne Arbeitsweise eures Lexers/Parser angepasst sein.

Naja Brikenfeld, danke für die Information das man reSTR "nicht" tokenisieren kann ;)

lg
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

Mittwoch 7. Februar 2007, 01:35

Hallo,

ich behaupte mal, mit re sollte man fast alles erschlagen.

Code: Alles auswählen

([\w\s]*)       # alphanumeric character any whitespace
                    # zero or more times; hier braucht nix stehen!! Die (::) können am Anfang der Zeile stehen
(::)                # ::
\s*                # zero or more times whitespace
\n                 # NewLine im String wie 'abc\nxyz'
\s*
\n
(.*)
(\n)
Die Aufgabe bleibt mir noch verborgen. Pythonanfänger.
a) Wo beginnt die _re_, mitten in der Zeile?
b) Wo endet sie, nach drei Zeilenumbrüchen?
c) Wer braucht die einfangenden Klammern?
d) Wozu split, wenn in Klammern eingefangen wird?

Gruß P.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Mittwoch 7. Februar 2007, 08:45

sape hat geschrieben: Man kann also reSTR nicht mit RegExp tokenisieren und ist "zu" Komplex?
Finde es interessant wenn man sich mal folgende Source von dir anschaut: http://trac.pocoo.org/browser/pygments/ ... order=name
Der Lexer dort ist ja auch in keinster Weise ein vollständiger Lexer für ReST, da werden nur die grundlegenden Markupbestandteile abgedeckt und es macht auch nichts, wenn mal ein Konstrukt nicht oder falsch erkannt wird.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
Luzandro
User
Beiträge: 87
Registriert: Freitag 21. April 2006, 17:03

Mittwoch 7. Februar 2007, 09:35

sape hat geschrieben:
Luzandro hat geschrieben: Wieso verwendest du hier eigentl. split? Der erste und der letzte Teil deiner Liste haben somit überhaupt nichts mit deiner Regex zu tun und sind nur der gesplittete Teil mit den einzelnen Gruppen dazwischen.
Exakt und ist auch so gewollt.
http://www.python-forum.de/post-57598.html#57598

Also ich habe verschiedene RegExp und verwende split auf den ganzen text. Vorfinden tu ich dann einzelne tokens die dann der Lexer überprüft und mit Type zurückgibt. Mehrdeutigkeiten kann der Lexer auch schon auflösen.
Ah ok, ich verstehe zwar jetzt, was du willst, nur in diesem Kontext ist das split ja dennoch unnötig, weil du willst ja hier erstmal diesen Literal Block als einzelnen Token matchen - ich verstehe daher jetzt auch nicht deinen Bsp-output mit den einzelnen aufgsplitteten Zeilen, denn IMHO solltest du als Ergebnis den gesamten Text bekommen.
Auch deine Definition am Anfang, was die Struktur betrifft ist eigentlich nicht richtig:
A paragraph containing only two colons...
Es müsste also in etwa so aussehen:

Code: Alles auswählen

>>> source = """This is a typical paragraph.  An indented literal block follows.

::

    for a in [5,4,3,2,1]:   # this is program code, shown as-is
        print a
    print "it's..."
    # a literal block continues until the indentation ends

This text has returned to the indentation of the first paragraph,
is outside of the literal block, and is therefore treated as an
ordinary paragraph."""

>>> pattern = re.compile(r'(?P<indent>^[ \t]*)(::[ \t]*\n+)((?:(?P=indent)[ \t]+.*\n)+)', re.MULTILINE)
>>> pattern.findall(source)
[('', '::\n\n', '    for a in [5,4,3,2,1]:   # this is program code, shown as-is\n        print a\n    print "it\'s..."\n    # a literal block continues until the indentation ends\n')]
[url=http://www.leckse.net/artikel/meta/profilieren]Profilieren im Netz leicht gemacht[/url]
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Mittwoch 7. Februar 2007, 11:57

@Luzandro: Vielen Dank für deinen Post und dein Beispiel das genau das macht was ich wollte. Ich sehe das ich mich mit dem re Modul mehr beschäftigen muss! Ich werde das ab heute Abend nachholen und denke das ich das gegen ende der Woche einigermaßen drauf habe.

Das mit den symbolic group name und das man sich darauf an anderen stellen im restring beziehen kann ist Super (auch die abfrage mit ``.groupdict``). --Ich werde die ausdrücke gleich mit SGN machen, dann habe ich gleich einen Tokenstrom mit "Type" bei Benutzung von ``m.groupdict()`` und spare mir den Teil im Lexer der dafür zuständig sein sollte. Werde das gleich so nutzen:

Code: Alles auswählen

 for m in pattern.finditer(source):
       print m.groupdict()
A paragraph containing only two colons...
Es sind auch solche Sachen erlaubt, auch wenn es da nicht steht.

Code: Alles auswählen

foobar::
    
    for i in xrange(100):
        print i
Das sieht man öfter in Modulen die reSTR benutzen für die API-Doc die mit Epydoc erstellt werden.


lg
sape
User
Beiträge: 1157
Registriert: Sonntag 3. September 2006, 12:52

Mittwoch 7. Februar 2007, 12:18

PmanX hat geschrieben:[...]
Die Aufgabe bleibt mir noch verborgen. Pythonanfänger.
a) Wo beginnt die _re_, mitten in der Zeile?
b) Wo endet sie, nach drei Zeilenumbrüchen?
c) Wer braucht die einfangenden Klammern?
d) Wozu split, wenn in Klammern eingefangen wird?
[...]
Vorweg: Der ansatz von mir war shclecht. ``split`` ist tatsächlich dafür sehr ungeeignet und die Methode von Luzandro ist viel besser.

a) Ja so ist es gedacht gewesen.
b) Da der ausdruck den ich verwendet habe fehlerhaft ist, endet es an der stelle wo ein (\n) ist wenn ([\w\s]*) (::)\s*\n\s*\n zutrift.
c), d) Brauchen tut man die Klammern immer wenn man das Ergebnis des matches mit drin haben will:

Mit Klammern:

Code: Alles auswählen

source = """
    *This* text has returned *to* the indentation of the *first paragraph*,
is outside of the literal block, and is therefore treated as *an*
ordinary* paragraph."""
    
pattern = re.compile(r'(\*)([\w \t]+)(\*)', re.MULTILINE | re.UNICODE | re.VERBOSE)
for m in pattern.finditer(source):
    print m.groups()
output:

Code: Alles auswählen

('*', 'This', '*')
('*', 'to', '*')
('*', 'first paragraph', '*')
('*', 'an', '*')
Ohne Klammern:

Code: Alles auswählen

source = """
    *This* text has returned *to* the indentation of the *first paragraph*,
is outside of the literal block, and is therefore treated as *an*
ordinary* paragraph."""
    
pattern = re.compile(r'\*[\w \t]+\*', re.MULTILINE | re.UNICODE | re.VERBOSE)
for m in pattern.finditer(source):
    print m.groups()
output:

Code: Alles auswählen

()
()
()
()
lg
Antworten