regex / finden und ersetzen durch unterschiedliche strings

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.
Antworten
mmg
User
Beiträge: 11
Registriert: Dienstag 15. Mai 2007, 14:47

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
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.
mmg
User
Beiträge: 11
Registriert: Dienstag 15. Mai 2007, 14:47

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
BlackJack

Dann hast Du bei denen Tests etwas falsch gemacht. Die Funktion wird für *jeden* Treffer aufgerufen.
mmg
User
Beiträge: 11
Registriert: Dienstag 15. Mai 2007, 14:47

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
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

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
poker
User
Beiträge: 146
Registriert: Donnerstag 20. September 2007, 21:44

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"
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.
mmg
User
Beiträge: 11
Registriert: Dienstag 15. Mai 2007, 14:47

danke euch beiden, funktioniert jetzt problemlos. :)

grüße,
marco
Antworten