Seite 1 von 1

regex / finden und ersetzen durch unterschiedliche strings

Verfasst: Freitag 21. September 2007, 18:11
von mmg
hi,

ich hänge da an einer sache, bei der ich vielleicht nur einen kleinen tritt in die richtige richtung brauche...

quelltext:

Code: Alles auswählen

def imageparser(text):
    try:
        p = re.compile( ':IMAGE:(?P<photo_id>.*?):(?P<photosize>.*?):(?P<css_class>.*?):' )
        m = p.search(text)
        photo = Photo.objects.get(pk=m.group('photo_id'))
        url = photo._get_SIZE_url(m.group('photosize'))
        text = p.sub(r'<img class="\g<css_class>" src="%s">' % url, text)
        return text
    except:
        return text
ich habe eine funktion, der ich einen text übergebe, und die aus diesem text mittels regex bestimmte ersetzungen vornimmt. so weit, so gut.

wenn im text also

Code: Alles auswählen

:IMAGE:123:small:left:
vorkommt, dann sollen die einzelnen bestandteile herausgelöst und basierend auf diesen bestandteilen dann eben ersetzungen vorgenommen werden.

meine funktion von oben findet zwar alle vorkommenden strings, ersetzt werden sie aber nur durch das ergebnis der ersten ersetzung.

ich glaub ich seh den wald vor lauter bäumen nicht...

danke und grüße,
marco

Verfasst: Freitag 21. September 2007, 18:59
von BlackJack
Der Quelltext macht genau was Du beschreibst, mit ``p.search(text)`` wird das erste Vorkommen gesucht, daraus eine Ersetzungszeichenkette gebastelt und dann mit ``re.sub`` diese eine, feste Zeichenkette für alle Vorkommen des Musters eingesetzt.

Man kann `sub()` auch eine Funktion als erstes Argument übergeben, die ein Match-Objekt bekommt und eine Zeichenkette zurückgeben muss. Die wird dann für jeden Treffer mit dem entsprechenden Match-Objekt aufgerufen.

Verfasst: Freitag 21. September 2007, 20:03
von mmg
danke für den tipp. allerdings wird meinen tests zufolge die funktion auch nur das allererste mal aufgerufen und das resultat daraus für alle weiteren ersetzungen genommen.

die einzige methode bei der das zuverlässig funktioniert sind regex-groups, die ich im replacement-string verwende, die werden bei jedem durchlauf zuverlässig ersetzt.

aber ich habe eben nicht alle werte zur hand, sondern muss manche davon erst irgendwo her nehmen :).

grüße,
marco

Verfasst: Freitag 21. September 2007, 20:31
von BlackJack
Dann hast Du bei denen Tests etwas falsch gemacht. Die Funktion wird für *jeden* Treffer aufgerufen.

Verfasst: Freitag 21. September 2007, 23:26
von mmg
ja, sieht ganz danach aus, irgendetwas habe ich wahrscheinlich übersehen. ich habe extra meine tests jetzt noch mal wiederholt, komme aber zum gleichen ergebnis...

quälcode:

Code: Alles auswählen

import re

def imagereplacer(m):
    try:
        (phid, url, phcss) = m.groups()
        return r'img class="%s" src="%s"' % (phcss, url)
    except:
        return ""

def imageparser(text):
    try:
        p = re.compile( ':IMAGE:(?P<photo_id>.*?):(?P<photosize>.*?):(?P<css_class>.*?):' )
        m = p.search(text)
        text = p.sub(imagereplacer(m), text)
        return text
    except:
        return text

print imageparser("""
	ein text
	1. auftreten:
	:IMAGE:123:small:left:
	2. auftreten:
	:IMAGE:234:big:right:
	und noch ein text
	""")
und das erzeugt bei mir folgende ausgabe:

Code: Alles auswählen

>c:\python25\pythonw -u "regextest.py"

	ein text
	1. auftreten:
	img class="left" src="small"
	2. auftreten:
	img class="left" src="small"
	und noch ein text
	
>Exit code: 0
noch eine idee für mich?

danke und grüße,
marco

Verfasst: Freitag 21. September 2007, 23:49
von poker
Hi,

schnell zusammengehackt und nicht optimiert.

Code: Alles auswählen

import re

def imagereplacer(m):
    try:
        phid, url, phcss, char = m.groups()
        return r'img class="%s" src="%s"' % (phcss, url)
    except:
        return ""

def imageparser(text):
    result = []
    char_stack = []
    to_stack = char_stack.append
    to_result = result.append
    p = re.compile(':IMAGE:(.*?):(.*?):(.*?):|(.)(?su)')
    for m in p.finditer(text):
        if m.groups()[3] is not None:
            to_stack(m.groups()[3])
        else:
            if char_stack:
               to_result("".join(char_stack))
            to_result(imagereplacer(m))
    return "".join(result)

print imageparser("""
    ein text
    1. auftreten:
    :IMAGE:123:small:left:
    2. auftreten:
    :IMAGE:234:big:right:
    und noch ein text
    """)

Code: Alles auswählen

    ein text
    1. auftreten:
    img class="left" src="small"
    ein text
    1. auftreten:
    
    2. auftreten:
    img class="right" src="big"
mfg

Verfasst: Samstag 22. September 2007, 00:11
von poker
Hi, eine bessere Lösung. Sollte flotter laufen :)

Code: Alles auswählen

import re


_image_re = re.compile(r':IMAGE:(.*?):(.*?):(.*?):')
def image_parser(text):
    rv = []
    last_pos = 0
    for m in _image_re.finditer(text):
        phid, url, phcss = m.groups()
        txt = text[last_pos:m.start()]
        if txt:
            rv.append(txt)
            last_pos = m.end()
        rv.append(r'img class="%s" src="%s"' %(phcss, url))
    return "".join(rv)
BTW: Das kompilieren des Patterns in der Funktion bringt keinerlei Geschwindigkeitsvorteil, weil es ja bei jedem Funktionsaufruf gemacht wird. Daher außerhalb der Funktion ausgelagert.

BTW2: Falls die id nicht benötigt wird, kann man gleich

Code: Alles auswählen

r':IMAGE:.*?:(.*?):(.*?):'
schreiben und kann dann ``url, phcss = m.groups()`` statt ``phid, url, phcss = m.groups()`` schreiben.

mfg

P.S.: Musst aber teste ob alle fälle berücksichtigt werden.

EDIT:
Nimm lieber die regexe

Code: Alles auswählen

r':IMAGE:\s*(.*?):\s*(.*?):\s*(.*?):'
, dann hast du keine blanks drine wenn z.B.

Code: Alles auswählen

:IMAGE:123:         small:   left:
im text vorkommt :)

Also statt

Code: Alles auswählen

img class="     left" src="   small"
kommt dann korrekt das raus:

Code: Alles auswählen

img class="left" src="small"

Verfasst: Samstag 22. September 2007, 08:01
von BlackJack
@mmg: Du musst die *Funktion* als Argument übergeben. Was Du da machst ist wieder genau das selbe wie vorher. Du suchst immer noch selbst mit `search()` nach dem ersten Treffer und setzt den dann für alle ein. Einfach *nur die Funktion* übergeben!

Code: Alles auswählen

import re

def imagereplacer(m):
    (phid, url, phcss) = m.groups()
    return 'img class="%s" src="%s"' % (phcss, url)

def imageparser(text):
    image_re = re.compile(':IMAGE:(?P<photo_id>.*?)'
                          ':(?P<photosize>.*?)'
                          ':(?P<css_class>.*?):')
    return image_re.sub(imagereplacer, text)

print imageparser("""
    ein text
    1. auftreten:
    :IMAGE:123:small:left:
    2. auftreten:
    :IMAGE:234:big:right:
    und noch ein text
    """)
Dann muss man auch nicht soviel selbst tun wie in poker's Lösung.

Verfasst: Samstag 22. September 2007, 10:23
von mmg
danke euch beiden, funktioniert jetzt problemlos. :)

grüße,
marco