Einen String parsen

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
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Es gibt wiedermal was zu Parsen und mir fehlt da irgendwo der Anfang:

Code: Alles auswählen

replace "Hello" "he|he  hehe" | replace '$NAME$' user.username | addslashes
Soll zu der Datenstruktur werden:

Code: Alles auswählen

[['replace', '"Hello"', '"he|he  hehe"'], ['replace', "'$NAME$', 'user.username'], ['addslashes']]
Jemand eine Idee? Innerhalb der Zeichenketten soll man auch mit einem Backslash escapen können, folgedem wird das wohl mit einer Regex nicht wirklich funktionieren.
TUFKAB – the user formerly known as blackbird
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

Wir wäre es zum Anfang mit

Code: Alles auswählen

s = "replace \"Hello\" \"he|he  hehe\" | replace '$NAME$' user.username | addslashes".split()

print s

res=[]
tmp=[]
for e in s:
	if e == '|':
		res.append(tmp)
		tmp=[]
	else:
		tmp.append(e)

print res
Jetzt solltest du das split noch durch eine RegEx ersetzten, das die Anführungszeichenberücksichtig...
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

ProgChild hat geschrieben: Jetzt solltest du das split noch durch eine RegEx ersetzten, das die Anführungszeichenberücksichtig...
Damit verliere ich aber die Leerzeichen in den Strings. :(
TUFKAB – the user formerly known as blackbird
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

blackbird hat geschrieben:Damit verliere ich aber die Leerzeichen in den Strings. :(
Das solltest du mit der RegEx berücksichtigen...
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

So...

Das is doch net so schwer...

Code: Alles auswählen

import re
s = "replace \"Hello\" \"he|he  hehe\" | replace '$NAME$' user.username | addslashes"

items = re.compile('".*"|\w+|\|').findall(s)

res=[]
tmp=[]
for e in items:
    if e == '|':
        res.append(tmp)
        tmp=[]
    else:
        tmp.append(e)

print res 
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

Funktinoiert leider nicht. zwischen den Pipe Symbolen müssen übrigens keine leerzeichen vorkommen, was die Sache nicht gerade einfacher macht.
TUFKAB – the user formerly known as blackbird
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

blackbird hat geschrieben:Funktinoiert leider nicht. zwischen den Pipe Symbolen müssen übrigens keine leerzeichen vorkommen, was die Sache nicht gerade einfacher macht.
War noch ein kleiner Fehler drinn, aber sonst klappts. Wenn du mir nicht schreibst, wass nich funktionier, dann kann ich dir nicht helfen. In dem Programm von mir müssen außerdem auch keine Whitespaces vor, oder nach dem | Sybol sein.

Code: Alles auswählen

import re
s = "replace "Hello" "he|he  hehe" | replace '$NAME$' user.username | addslashes"

items = re.compile('".*?"|[\w|$|\.]+|\|').findall(s)

res=[]
tmp=[]
for e in items:
    if e == '|':
        res.append(tmp)
        tmp=[]
    else:
        tmp.append(e)
res.append(tmp)

print res 
Das Resultat:

Code: Alles auswählen

> python whitespace_parse.py 
[['replace', '"Hello"', '"he|he  hehe"'], ['replace', '$NAME$', 'user.username'], ['addslashes']]
Zuletzt geändert von ProgChild am Sonntag 1. Januar 2006, 14:08, insgesamt 2-mal geändert.
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

ProgChild hat geschrieben:
blackbird hat geschrieben:Funktinoiert leider nicht. zwischen den Pipe Symbolen müssen übrigens keine leerzeichen vorkommen, was die Sache nicht gerade einfacher macht.
War noch ein kleiner Fehler drinn, aber sonst klappts. Wenn du mir nicht schreibst, wass nich funktionier, dann kann ich dir nicht helfen.
Ausgabe:

Code: Alles auswählen

[['replace', '"Hello"', '"he|he  hehe"'], ['replace', 'NAME', 'user', 'username']]
Ist nich das, was rauskommen soll.
TUFKAB – the user formerly known as blackbird
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

blackbird hat geschrieben:Ist nich das, was rauskommen soll.
Ich habs noch mal überarbeitet. Außerdem ist das nur ein Denkanstoß für dich, den du weiter ausbauen sollst. :roll:
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

ProgChild hat geschrieben:
blackbird hat geschrieben:Ist nich das, was rauskommen soll.
Ich habs noch mal überarbeitet. Außerdem ist das nur ein Denkanstoß für dich, den du weiter ausbauen sollst. :roll:
:roll: Danke ich weiß. Aber nach dem System komm ich nicht weit. Mein aktueller Versuch ist das ganze Zeichen für Zeichen durchzugehen und in Token einzuteilen. Ist nur verdammt langsam und 300 Zeilen lang.
TUFKAB – the user formerly known as blackbird
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

So. Meine aktuelle Lösung ist das hier:

Code: Alles auswählen

# -*- coding: utf-8 -*-
"""
    Filter Parser
    =============
    
    source:
        replace "Hello" "he|he  hehe" | replace '$NAME$' user.username
    result:
        [['replace', '"Hello"', '"he|he  hehe"'], ['replace', "'$NAME$'", 'user.username']]
"""

TOKEN_CDATA = 0
TOKEN_DOUBLE_QUOTED_STRING = 1
TOKEN_SINGLE_QUOTED_STRING = 2
TOKEN_DOUBLE_QUOTED_BACKSLASH = 3
TOKEN_SINGLE_QUOTED_BACKSLASH = 4


class EmptyTokenException(Exception): pass


class FilterLexer(object):

    def __init__(self):
        self._buffer = []
        self._bufpos = 0
        self._state = TOKEN_CDATA
        self._valbuf = ''
        self._tmpdata = []
        self.data = []

    def feed(self, data):
        self._buffer.append(data)
        try:
            self.lex()
        except EmptyTokenException:
            pass

    def finish(self):
        self.feed('|')

    def pop(self):
        while self._buffer and self._bufpos >= len(self._buffer[0]):
            del self._buffer[0]
            self._bufpos = 0
        if not self._buffer:
            raise EmptyTokenException
        rv = self._buffer[0][self._bufpos]
        self._bufpos += 1
        return rv

    def lex(self):
        while True:
            if self._state == TOKEN_CDATA:
                while True:
                    data = self.pop()
                    if data == '|':
                        self.handle_token(TOKEN_CDATA, self._valbuf)
                        self.handle_switch()
                        self._valbuf = ''
                    elif data in ' \t\r\n':
                        self.handle_token(TOKEN_CDATA, self._valbuf)
                        self._valbuf = ''
                    elif data == '"':
                        self.handle_token(TOKEN_CDATA, self._valbuf)
                        self._state = TOKEN_DOUBLE_QUOTED_STRING
                        self._valbuf = ''
                        break
                    elif data == "'":
                        self.handle_token(TOKEN_CDATA, self._valbuf)
                        self._state = TOKEN_SINGLE_QUOTED_STRING
                        self._valbuf = ''
                        break
                    else:
                        self._valbuf += data
                        
            if self._state == TOKEN_DOUBLE_QUOTED_STRING:
                while True:
                    data = self.pop()
                    if data == '\\':
                        self._state = TOKEN_DOUBLE_QUOTED_BACHSLASH
                        break
                    elif data == '"':
                        self.handle_token(TOKEN_DOUBLE_QUOTED_STRING, self._valbuf)
                        self._state = TOKEN_CDATA
                        self._valbuf = ''
                        break
                    else:
                        self._valbuf += data
                        
            if self._state == TOKEN_SINGLE_QUOTED_STRING:
                while True:
                    data = self.pop()
                    if data == '\\':
                        self._state = TOKEN_SINGLE_QUOTED_BACHSLASH
                        break
                    elif data == "'":
                        self.handle_token(TOKEN_SINGLE_QUOTED_STRING, self._valbuf)
                        self._state = TOKEN_CDATA
                        self._valbuf = ''
                        break
                    else:
                        self._valbuf += data
                        
            if self._state == TOKEN_DOUBLE_QUOTED_BACKSLASH:
                self._valbuf += self.pop()
                self._state = TOKEN_DOUBLE_QUOTED_STRING
                
            if self._state == TOKEN_SINGLE_QUOTED_BACKSLASH:
                self._valbuf += self.pop()
                self._state = TOKEN_SINGLE_QUOTED_STRING
                

    def handle_token(self, token_type, data):
        if token_type == TOKEN_CDATA and data:
            self._tmpdata.append(data)
        elif token_type == TOKEN_SINGLE_QUOTED_STRING:
            self._tmpdata.append('\'%s\'' % data)
        elif token_type == TOKEN_DOUBLE_QUOTED_STRING:
            self._tmpdata.append('"%s"' % data)

    def handle_switch(self):
        self.data.append(self._tmpdata)
        self._tmpdata = []


def parse_filter(filterline):
    lexer = FilterLexer()
    lexer.feed(filterline)
    lexer.finish()
    return lexer.data


print parse_filter('replace "Hello" "he|he  hehe" | replace \'$NAME\' user.username')
Aber wie gesagt. Langsam ist.
TUFKAB – the user formerly known as blackbird
XT@ngel
User
Beiträge: 255
Registriert: Dienstag 6. August 2002, 14:36
Kontaktdaten:

Hi blackbird,
Woher kommt dieser string:

Code: Alles auswählen

replace "Hello" "he|he  hehe" | replace '$NAME$' user.username | addslashes
Ich frag mich gerade ob das replace und addslashes auch eine Funktion haben. Oder sind nur die | entscheidend die sich nicht zwischen zwei " befinden?

Ist die Länge vorgegeben also

Code: Alles auswählen

blubblub | blablabla | XXXX
Also drei "Felder"?

Lass mal ein paar Infos springen :wink:


MfG
Andreas
mawe
Python-Forum Veteran
Beiträge: 1209
Registriert: Montag 29. September 2003, 17:18
Wohnort: Purkersdorf (bei Wien [Austria])

Hi!

Vielleicht so?

Code: Alles auswählen

s = '''replace "Hello" "he|he hehe" | replace '$NAME$' user.username | addslashes'''
a = [[]]
quoted = False
tmp = ""

for sign in s:
    if sign == '"': quoted = not quoted
    if sign == '|' and not quoted:
        a.append([])
    elif sign == ' ' and not quoted:
        if tmp:
            a[-1].append(tmp)
            tmp = ""
    else:
        tmp += sign
a[-1].append(tmp)
Gruß, mawe
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

XT@ngel hat geschrieben:Hi blackbird,
Woher kommt dieser string:

Code: Alles auswählen

replace "Hello" "he|he  hehe" | replace '$NAME$' user.username | addslashes
Ich frag mich gerade ob das replace und addslashes auch eine Funktion haben. Oder sind nur die | entscheidend die sich nicht zwischen zwei " befinden?
Klar ist das mit einer Funktion verbunden :-) Das ganze ist das Filtersystem von Jinja, der Parser findet sich atm hier: http://wsgiarea.pocoo.org/trac/browser/ ... rparser.py

Die Pipe ist einfach zum weiterschicken an die nächste Funktion gedacht. Man kann dann im Template sowas machen:

Code: Alles auswählen

{{ liste|removedouble|sort|slice 4:10|join ', ' }}
@mawe: schaut gut aus, aber ein handling für einfache gequotete Strings ist nicht dabei :(
TUFKAB – the user formerly known as blackbird
BlackJack

Hilft das `shlex` Modul eventuell beim "Tokenizen"?
mitsuhiko
User
Beiträge: 1790
Registriert: Donnerstag 28. Oktober 2004, 16:33
Wohnort: Graz, Steiermark - Österreich
Kontaktdaten:

BlackJack hat geschrieben:Hilft das `shlex` Modul eventuell beim "Tokenizen"?
argh. so einfach. Wiedermal die Batterien übersprungen. Das ist wirklich was Feines. Blöderweise fehlen mir die dazugehörigen Quotes wodurch ich nicht rausfinden kann, ob das jetzt eine Stringkonstante oder eine Variable ist :cry:
TUFKAB – the user formerly known as blackbird
Antworten