Wieder mal Regular Expressions

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
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Hallo!
Ich möchte eine Zeile folgender Form

Code: Alles auswählen

(x1 * '\s') + (x2 * '\S') + (x3 * '\s' + x4 * '\S')  + (x5 * '\s' + x6 * '\S') + ... + xn * '\s'
\s = Whitespace, \S = Non-Whitespace, x? in [0, 1, 2, ...], aber x2 in [1, 2, 3, ...]
in die abwechselnden Whitespace- und Non-Whitespace-Gruppen aufteilen, wobei mich eigentlich nur deren Längen interessieren.
Es sollen also z.B. folgende Zeilen zerlegt werden können:

Code: Alles auswählen

10 * ' ' + 10 * '+' + 10 * ' ' + 10 * '-' + 10 * ' '
und
10 * '+'
Ich habe gehofft, es mit REs lösen zu können. Meine Versuche sind aber bisher kläglich gescheitert, z.B.:

Code: Alles auswählen

'^(\s*)((\S+)(\s*))+$'
Hat jemand eine Idee oder muss ich es doch über eine selbst geschreibene Funktion lösen?
Danke
HWK
Zuletzt geändert von HWK am Freitag 23. Februar 2007, 22:56, insgesamt 1-mal geändert.
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

Benutze finditer():

Code: Alles auswählen

rex = re.compile(r"(\s*)(\S*)")
for match in rex.finditer(text):
    len_space = len(match.group(1))
    len_notspace = len(match.group(2))
    ...
Mit "\S*" kannst du Leerzeichen am Ende mit derselben RE abdecken,
bekommst aber am Schluss immer einen leeren Match.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Dein Ausdruck funktioniert ja.

Nur dein Problem wird nicht der reguläre Ausdruck selbst sein sondern das Resultat. Wiederholte Gruppen, bei dir ((\S+)(\s*))+, geben im Resultat nur eine Matchgroup zurück, der Rest wurde bei jeder Wiederholung überschrieben.

Als Resultat bekommst du:
1. Gruppe: der erste Block Leerzeichen
2. Gruppe: letzer Match der Gruppe non-whitespaces;whitespaces
3. Gruppe: non-whitespaces der 2. Gruppe
4. Gruppe: die letzten whitespaces der Gruppe 2

Gruss

*edit* ja da war birkenfeld mal wieder ein wenig schneller ;)
nimm keinen Ausdruck für alles sondern nur den Wiederholten teil
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Prima! Vielen Dank.
Ich hatte es zwar zwischenzeitlich schon ohne REs gelöst, aber ich habe wieder etwas dazu gelernt. Hier einmal beide Varianten:

Code: Alles auswählen

    def my_split(self, text):
        flag = text[0].isspace()
        cnt = 0
        result = []
        for c in text:
            if c.isspace() == flag:
                cnt += 1
            else:
                result.append(cnt)
                flag = not flag
                cnt = 1
        result.append(cnt)
        return result

    def my_split(self, text):
        result = []
        rex = re.compile('(\s*)(\S*)')
        for match in rex.finditer(text):
            result.append(len(match.group(1)))
            result.append(len(match.group(2)))
        for i in (0, -3):
            if result[i] == 0:
                del(result[i])
        return result[:-2]
MfG
HWK
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

Hallo,

ich denke, die RegEx funktioniert nicht.

Code: Alles auswählen

#
>>> rc = re.compile(r"""
  (\s*) # 0 or more repetitions
  (\S*) # 0 or more repetitions
""", re.VERBOSE)
Diese RegEx muß auf gar nichts passen!
Wenn Whitespaces keine Trenner sind, greift sich \S* gleich
den nächsten Token.

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

Hast du deine Gedanken auch ausprobiert?
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

PmanX hat geschrieben:Hallo,

ich denke, die RegEx funktioniert nicht.
Doch! Sie tut genau das, was ich brauche. Sie liefert mir die Längen der Blöcke mit Whitespaces bzw. Non-Whitespaces zurück. Die Ergebnisse sind identisch zu meiner Alternative. Es mag sein, dass Sie bei anderen Ausgangsstrings nicht funktionieren würde, die bearbeiteten Strings erfüllen aber feste Kriterien. Sie bestehen nur abwechselnd aus Blöcken von Leerzeichen und anderen ASCII-Zeichen. Ich hätte also statt '\s*' auch ' *' nehmen können.
MfG
HWK
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

birkenfeld hat geschrieben:Hast du deine Gedanken auch ausprobiert?
Es ist die RegEx, halt nur re.X. Oder anders, welche Gedanken habe ich unterlassen?

HWK:
Beim Gebrauch von regulären Ausdrücken und bei deren Anwendung auf Daten in bestimmten Situationen werden meist stillschweigend Annahmen getroffen, und es ist oft notwendig, sich diese bewußt zu machen.
Um die RegEx zu beschreiben, fehlen einige Informationen.
Konkret: Soll zeilenorientiert gearbeitet werden und soll der erste Token von Whitespaces wahlfrei sein?
Da würde ich zweistufig arbeiten.
1) Whitespaces am Zeilenanfang entfernen.
2) Über Zeile iterieren und die Token auswerten.
Benutzeravatar
Luzandro
User
Beiträge: 87
Registriert: Freitag 21. April 2006, 17:03

Wenn ich dich richtig verstanden habe, müsste auch das reichen:

Code: Alles auswählen

map(len, re.findall(r"\S+|\s+", text))
[url=http://www.leckse.net/artikel/meta/profilieren]Profilieren im Netz leicht gemacht[/url]
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

Code: Alles auswählen

(x1 * '\s' + x2 * '\S')        # (\s*\S+)
(x3 * '\s' + x4 * '\S')        # (\S*\s*)
(x3 * '\s' + x4 * '\S')

x? in [0, 1, 2, ...]     # .*
x2 in [1, 2, 3, ...]     # \S+
Hier braucht nur ein Token mit NonWhitespaces vorkommen.

Was ich nicht verstehe, wenn ein Token aus (Whitespaces & NonWhitespaces) bestehen soll, *EDIT* dann sollte x1 \s+ sein.

Wenn ich die Anforderung richtig erfasst habe, ist diese Lösung ausreichend und gefällt :!:
Zuletzt geändert von PmanX am Freitag 23. Februar 2007, 23:58, insgesamt 1-mal geändert.
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Luzandro hat geschrieben:Wenn ich dich richtig verstanden habe, müsste auch das reichen:

Code: Alles auswählen

map(len, re.findall(r"\S+|\s+", text))
Tatsächlich. Das ist echt toll.
PmanX hat geschrieben:Was ich nicht verstehe, wenn eine Token aus (Whitespaces & NonWhitespaces) bestehen soll, *EDIT* dann sollte x1 \s+ sein.
Nein. Whitespaces am Anfang sind nicht zwingend. Es handelt sich auch nicht um Tokens.
PmanX hat geschrieben:Wenn ich die Anforderung richtig erfasst habe, ist diese Lösung ausreichend und gefällt.
Welche Lösung?
PmanX
User
Beiträge: 123
Registriert: Donnerstag 25. Januar 2007, 13:50
Wohnort: Germany.BB.LOS
Kontaktdaten:

Wie wäre es mit

Code: Alles auswählen

map(len, re.findall(r'\S+|\s+', re.sub(r'^\s+', '', line)))
?
Benutzeravatar
birkenfeld
Python-Forum Veteran
Beiträge: 1603
Registriert: Montag 20. März 2006, 15:29
Wohnort: Die aufstrebende Universitätsstadt bei München

re.sub(r'^\s+', '', line) ist schneller und einfacher line.lstrip(),
ansonsten ist das doch ne schöne Lösung.
Dann lieber noch Vim 7 als Windows 7.

http://pythonic.pocoo.org/
Benutzeravatar
HWK
User
Beiträge: 1295
Registriert: Mittwoch 7. Juni 2006, 20:44

Ich brauche aber auch die Länge des evtl. am Anfang stehenden Whitespace-Blocks. Deshalb ist Luzandros Lösung die für mich passende und auch noch kürzer.
Allen herzlichen Dank
HWK
Antworten