Regex in Python, aber nur einen Teil des Musters ersetzen

Aug 17 2020

Alle Beispiele, die ich beim Stapelüberlauf gefunden habe, sind zu kompliziert, als dass ich sie rückentwickeln könnte .

Betrachten Sie dieses Spielzeugbeispiel

s = "asdfasd a_b dsfd"

ich möchte s = "asdfasd a'b dsfd"

Das heißt: Suchen Sie zwei durch einen Unterstrich getrennte Zeichen und ersetzen Sie diesen Unterstrich durch einen Apostroph

Versuch:

re.sub("[a-z](_)[a-z]","'",s)
# "asdfasd ' dsfd"

Ich dachte die ()sollten dieses Problem lösen?

Noch verwirrender ist die Tatsache, dass wir den Charakter, den wir ersetzen möchten, anscheinend erfolgreich gefunden haben:

re.findall("[a-z](_)[a-z]",s)
#['_']

warum wird das nicht ersetzt?

Vielen Dank

Antworten

3 Marat Aug 17 2020 at 00:27

Verwenden Sie Vorausschau- und Rückblickmuster:

re.sub("(?<=[a-z])_(?=[a-z])","'",s)

Vorwärts- / Rückwärtsmuster haben eine Breite von Null und ersetzen daher nichts.

UPD:

  • Das Problem war, dass re.subder gesamte übereinstimmende Ausdruck einschließlich des vorhergehenden und des folgenden Buchstabens ersetzt wird.
  • re.findallstimmte immer noch mit dem gesamten Ausdruck überein, hatte aber auch eine Gruppe (die Klammer im Inneren), die Sie beobachtet haben. Das ganze Match war stilla_b
  • Lookahead / Lookbehind-Ausdrücke prüfen, ob der Suche ein Muster vorausgeht / folgt, schließen es jedoch nicht in die Übereinstimmung ein.
  • Eine andere Möglichkeit bestand darin, mehrere Gruppen zu erstellen und diese Gruppen in den Ersatz einzufügen: re.sub("([a-z])_([a-z])", r"\1'\2", s)
2 RyszardCzech Aug 17 2020 at 02:48

Bei der Verwendung re.submuss der zu behaltende Text erfasst werden, der zu entfernende Text jedoch nicht.

Verwenden

re.sub(r"([a-z])_(?=[a-z])",r"\1'",s)

Siehe Beweis .

ERLÄUTERUNG

NODE                     EXPLANATION
--------------------------------------------------------------------------------
  (                        group and capture to \1:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of \1
--------------------------------------------------------------------------------
  _                        '_'
--------------------------------------------------------------------------------
  (?=                      look ahead to see if there is:
--------------------------------------------------------------------------------
    [a-z]                    any character of: 'a' to 'z'
--------------------------------------------------------------------------------
  )                        end of look-ahead

Python-Code :

import re
s = "asdfasd a_b dsfd"
print(re.sub(r"([a-z])_(?=[a-z])",r"\1'",s))

Ausgabe:

asdfasd a'b dsfd
ShinNShirley Sep 30 2020 at 14:33

Das re.subwird alles ersetzen, was es gefunden hat.

Es gibt eine allgemeinere Möglichkeit, Ihr Problem zu lösen, und Sie müssen Ihren regulären Ausdruck nicht erneut ändern.

Code unten:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0)) if _reg else r

# 
re.sub(reg,repl, s)

Ausgabe: 'Data: year=am_replace_str, monthday=am_replace_str, month=am_replace_str, some other text'

Wenn Ihr Fall keine Gruppen enthält, kann Ihr Code natürlich folgendermaßen aussehen:

import re


s = 'Data: year=2018, monthday=1, month=5, some other text'
reg = r"year=(\d{4}), monthday=(\d{1}), month=(\d{1})"


r = "am_replace_str"
def repl(match):
    _reg = "|".join(match.groups())
    return re.sub(_reg, r,match.group(0))

# 
re.sub(reg,repl, s)