Eckige Klammer mit Inhalt ersetzen - regex

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.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Hi zusammen,

ich mal wieder.
Ich würde gern aus dieser row2 =
[Hasse, H.] Univ Kaiserslautern, Lab Engn Thermodynam, D-67663 Kaiserslautern, Germany; [Ryll, O.; Blagov, S.] BASF SE, D-67056 Ludwigshafen, Germany
gerne die Eckigen Klammern samt Inhalt ersetzen, gehen "". Also das Ziel sollte sein:
Univ Kaiserslautern, Lab Engn Thermodynam, D-67663 Kaiserslautern, Germany; BASF SE, D-67056 Ludwigshafen, Germany
Idealerweise natürlich auch noch das Leerzeichen danach und ggf. davor, aber das ist sicherlich jenseits meiner Künste ;)
So hab ich versucht aber es funzt nicht:

Code: Alles auswählen

row2 = [cell.replace('\[/\w+/\]', '') for cell in row2]
Jemand eine Idee?

LG
Benutzeravatar
pillmuncher
User
Beiträge: 1484
Registriert: Samstag 21. März 2009, 22:59
Wohnort: Pfaffenwinkel

Das Stichwort, nach dem du suchst, heißt non-greedy.
In specifications, Murphy's Law supersedes Ohm's.
BlackJack

@kidchino: Dazu würde ich ein entsprechendes Werkzeug zum Entwickeln von regulären Ausdrücken verwenden. Es gibt da mehrere Desktopanwendungen und auch Online-Anwendungen im Internet. Da kannst Du den Text einfügen und dann den regulären Ausdruck entwickeln und siehst immer was der jeweils erkennt.

Neben dem „greedy”-Stichwort müsstest Du mal erklären was die '/' da machen und was '\w+' erkennt, und vor allem was es *nicht* erkennt.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Hi,
danke erstmal für die Tipps. Bastel gerade mal hier rum https://www.regex101.com/#python

Was haltet Ihr davon?
row2 = [cell.replace('([[A]).+?]', '') for cell in row2]
Auf regex101 klappt, in Python komischerweise nicht

Viele Grüße

UPDATE: Ich hab auch das globale "g" vergessen
Zuletzt geändert von kidchino am Dienstag 27. Januar 2015, 14:25, insgesamt 1-mal geändert.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Um reguläre Ausdrücke zu verwenden solltest du natürlich auch die Funktionen für reguläre Ausdrücke verwenden. Die replace-Methode eines Strings arbeitet nicht mit regulären Ausdrücken sondern ersetzt exakt das, was angegeben ist. Du suchst vermutlich re.sub.
BlackJack

@kidchino Der Ausdruck ist schon ein bisschen komisch. Wozu die Gruppe? Warum auch Sachen die mit einem grossen A anfangen statt mit einem '['?

`str.replace()` kennt keine regulären Ausdrücke. Dazu muss man das `re`-Modul verwenden.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Hi,

das müsste besser sein.

Code: Alles auswählen

'[[].+?]'
Wenn ich es global nutzen will, schreibe ich dann das g einfach hinten dran?

Code: Alles auswählen

'[[].+?]'g
VG
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@kidchino: wie man "re.sub" benutzt steht in der Dokumentation, Du brauchst also nicht zu raten und ungültige Syntax auszuprobieren. Meiner Meinung nach ist es viel einfacher zu verstehen, wenn man die eckigen Klammern escaped, statt sie in eine einelementige Menge zu packen. r'\[.+?\]'.
BlackJack

@kidchino: Ich würde wohl noch ein optionales Leerzeichen ans Ende des regulären Ausdrucks stellen, sonst hat man innerhalb der Zeichenketten wohl fast immer zwei aufeinanderfolgende Leerzeichen wo die Autorenreferenz stand.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Hi,
danke für die Tipps.

Code: Alles auswählen

row2 = [row[58], re.sub('[[].+?]', '', row[22])]
Analog zum vorherigen dann ja:
row2 = [re.sub('[[].+?]', '', row[22])]

Das "g" brauche ich ja bei re.sub gar nicht mehr.

@Sirius3: Mit dem escapen der Klammern hatte ich versucht aber es nicht bekommen, obwohl das von Dir
Sirius3 hat geschrieben: r'\[.+?\]'
schon einleuchten ist. Aber leider verstehe ich auch das "r" am Anfang nicht.

@BlackJack:
BlackJack hat geschrieben:optionales Leerzeichen
Das bekomme ich wohl nicht hin.

VG
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

Das war ja gar nicht so schwer ;)

Code: Alles auswählen

re.sub('[[].+?]\s?', '', row[22])
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

kidchino hat geschrieben:

Code: Alles auswählen

re.sub('[[].+?]\s?', '', row[22])
Backslashes müssen noch einmal escaped werden. Hier ist es jetzt nicht tragisch, aber spätestens bei Dingen wie '\n' fällst du auf die Nase. Du bräuchtest also prinzipiell ein '\\s'.

Hintergrund dafür ist die Stringverarbeitung. In einem String leitet ein Backslash eine Escape-Sequenz ein. Ein '\n' steht beispielsweise für einen Newline-Character. Um den Backslash selber zu erhalten musst du auch diesen wiederum mit einem Backslash escapen.

Code: Alles auswählen

>>> print('\\')
\
Den einzelnen Backslash, der aus der '\\' wird, braucht nun wiederum die Regex-Engine. Du hast in diesem Fall nur deshalb Glück, weil '\s' keine aktuell(!) gültige Escape-Sequenz ist und daher die Zeichen so wie eingegeben interpretiert werden.

Um das zu handlicher zu machen und der Backslash-Flut Einhalt zu gebieten gibt es raw-strings. Diese beginnen mit einem r und sorgen dafür, dass die Zeichen exakt so ankommen wie sie geschrieben wurden. Deine String-Definition sollte also r'[[].+?]\s?' lauten.

Schau dir noch mal das folgende Beispiel an.

Code: Alles auswählen

>>> print('x\ny')
x
y
>>> print(r'x\ny')
x\ny
Diese Info befindet sich übrigens auch ganz zu Beginn der Dokumentation des re-Moduls.
kidchino
User
Beiträge: 91
Registriert: Montag 17. November 2014, 14:18

@/me

Vielen Dank für die ausführliche und hilfreiche Antwort.
Das habe ich tatsächlich am Anfang der Doku überlesen. Es ist aber auch nicht ganz einfach verständlich wenn man nicht vom Fach ist und das englische erschwert es dann auch ein bisschen. Aber gerade diesen Teil hätte man schon verstehen können.
Danke!
Vg
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

Wenn man einen string, den man z. B. über einen user-Input oder aus config-Daten erhält, in einen raw-String umwandeln möchte: Was müsste man da tun? Ich habe verschiedene Lösungen gefunden, die zur Umwandlung eines strings in einen raw string entweder ``s.encode('unicode_escape')`` oder repr(s) empfehlen. Damit erhalte ich aber nur bei Escape oder Hex oder Oktal Sequenzen einen raw string, den ich als regex verwenden kann:

Code: Alles auswählen

>>> r'\n'
'\\n'
>>> '\n'.encode('unicode_escape')
'\\n'
Bei Regex Sequenzen müsste ich allerdings 'raw_unicode_escape' verwenden:

Code: Alles auswählen

>>> r'\w'
'\\w'
>>> '\w'.encode('string_escape')
'\\\\w'
>>> '\w'.encode('raw_unicode_escape')
'\\w'
Jetzt dachte ich mal wieder, ich sei superschlau ...

Code: Alles auswählen

>>> r'{}'.format('\w')
'\\w'
... was ich aber dann doch nicht bin:

Code: Alles auswählen

>>> r'{}'.format('\n')
'\n'
Bleibt also nur der Weg, vor der Umwandlung herauszufinden, um welche Art von Sequenz (Oktal Sequenz, Escape Sequenz ...) es sich handelt um dementsprechend den richtigen Codec zu verwenden? Oder gibt es eine einfachere Möglichkeit?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@mutetella: was willst Du denn erreichen?
Wenn Du beliebige Strings in regulären Ausdrücken verwenden willst, solltest Du re.escape verwenden, das escapet alle Zeichen, die in regulären Ausdrücken eine besondere Bedeutung haben, also auch Klammern oder *?+.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Sirius3

Code: Alles auswählen

>>> r'\w'
'\\w'
>>> re.escape('\w')
'\\\\w'
Alle Methoden, die angeboten werden, um einen String zu escapen, liefern keinen r'string'.

Code: Alles auswählen

>>> re.findall(r'(\w+)', 'Ich bin ein string')
['Ich', 'bin', 'ein', 'string']
>>> re.findall(re.escape('(\w+)'), 'Ich bin ein string')
[]
Wie gesagt, wenn ein raw string nicht hardcoded definiert ist, sondern eben 'von außen' kommt und als regex verwendet werden soll, wie escaped man den?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

mutetella hat geschrieben:Wenn man einen string, den man z. B. über einen user-Input oder aus config-Daten erhält, in einen raw-String umwandeln möchte: Was müsste man da tun?
Ich glaube du siehst da ein Problem wo keins ist.

Raw Strings und "normale" Strings sind im Speicher völlig identisch. Es geht hier nur um eine spezielle Notation im Code und zwar ausschließlich im Python-Programmcode. Wenn du von extern Daten einliest, dann kommen die Bytes so an wie du sie eingegeben hast. Da findet keine spezielle Verwandlung oder irgendein Escape-Mechanismus statt.

Code: Alles auswählen

>>> print(type('\\n'))
<class 'str'>
>>> print(type(r'\n'))
<class 'str'>
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

/me hat geschrieben:Ich glaube du siehst da ein Problem wo keins ist.
Wollte das jetzt fast auch schon glauben, :mrgreen: wobei ich tatsächlich nur auf "die falsche Seite" geschaut hab'. Ich hatte einfach mal spaßeshalber folgendes gemacht...

Code: Alles auswählen

def test():
    s = raw_input('String : ')
    while True:
        r = raw_input('Regex  : ')
        if not r:
            s = raw_input('String : ')
            r = raw_input('Regex  : ')
        print 'findall: {}'.format(re.findall(r, s))
... und mich gewundert, weshalb das dabei herauskommt:

Code: Alles auswählen

String : string with \n newline and \backslash.
Regex  : (\w+)
findall: ['string', 'with', 'n', 'newline', 'and', 'backslash']
Jetzt ging ich davon aus, dass "irgendwas" mit der regex nicht stimmen kann. Das "Problem" ist allerdings folgendes:

Code: Alles auswählen

>>> 'string with \n newline and \backslash.'
'string with \n newline and \x08ackslash.'
>>> raw_input('> ')
> string with \n newline and \backslash.
'string with \\n newline and \\backslash.'
>>> raw_input('> ').decode('string_escape')
> string with \n newline and \backslash.
'string with \n newline and \x08ackslash.'
Wenn ich also den via ``raw_input()`` eingelesenen string erstmal dekodiere, erhalte ich auch, was ich erwartet habe:

Code: Alles auswählen

def test():
    s = raw_input('String : ').decode('string_escape')
    while True:
        r = raw_input('Regex  : ')
        if not r:
            s = raw_input('String : ').decode('string_escape')
            r = raw_input('Regex  : ')
        print 'findall: {}'.format(re.findall(r, s))

Code: Alles auswählen

String : string with \n newline and \backslash.
Regex  : (\w+)
findall: ['string', 'with', 'newline', 'and', 'ackslash']
Regex  : 
String : string with \n newline and \\backslash.
Regex  : (\w+)
findall: ['string', 'with', 'newline', 'and', 'backslash']
Was ich jetzt allerdings nicht verstehe: Weshalb die Magie in ``raw_input()``? Wohin wird ``s`` eigentlich decodiert?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@mutetella: Wo ist denn da Magie? Du machst doch die ganze Magie, weil Du bei s Sonderzeichen per Escape eingeben willst. Also mußt Du auch die Eingabe des Users parsen und die Escape-Sequenzen umwandeln.
mutetella
User
Beiträge: 1695
Registriert: Donnerstag 5. März 2009, 17:10
Kontaktdaten:

@Sirius3
Mit Magie meine ich, dass ``raw_input()`` den eingegebenen string escaped:

Code: Alles auswählen

>>> s = 'after \t a tab'
>>> r = raw_input()
after \t a tab
>>> s
'after \t a tab'
>>> r
'after \\t a tab'
Um also einen string zu haben, der so behandelt wird, als wäre er r"" getaggt, muss ich das, was mir ``raw_input()`` escaped zurückgibt, erst wieder mit ``decode('string_escape')`` in seine "rohe" Fassung bringen.

Auf der Konsole verhält es sich genauso:

Code: Alles auswählen

$ read -p ">>" s; echo -e $s
>>after \t a tab
after t a tab
$ read -p ">>" s; echo -e $s
>>after \\t a tab
after    a tab
Ich versteh' nicht, weshalb bei der Eingabe eines strings eine control sequence nicht als solche eingegeben werden kann. Ich dachte immer, ein backslash muss dann escaped werden, wenn ich eben den backslash als Zeichen und nicht als Einleitung einer sequence interpretiert haben möchte.

Also nochmal: Warum ist ...

Code: Alles auswählen

>>> print 'after \t a tab'
after    a tab
... nicht dasselbe wie ...

Code: Alles auswählen

>>> print raw_input()
after \t a tab
after \t a tab
... ?

mutetella
Entspanne dich und wisse, dass es Zeit für alles gibt. (YogiTea Teebeutel Weisheit ;-) )
Antworten