2017-04-19 5 views
1

Ich habe eine Zeichenfolge, die etwa wie folgt aussieht:Ersetzen des Leerzeichen durch Unterstrich innerhalb einer übereinstimmenden Gruppe?

"(case when Campaign='Back to School' and VariableName='Total_FB_Spend' then VariableValue else 0 end) AS Back to School_Total_FB_Spend,(case when Campaign='Back to School' and VariableName='Total_FB_Imp' then VariableValue else 0 end) AS Back to School_Total_FB_Imp"

Ich mag würde Python Regex verwenden Leerzeichen in den Spaltennamen nach entfernen ‚Ende) AS‘ Charakter. Das Ergebnis sollte so etwas wie diese

"(case when Campaign='Back to School' and VariableName='Total_FB_Spend' then VariableValue else 0 end) AS Back_to_School_Total_FB_Spend,(case when Campaign='Back to School' and VariableName='Total_FB_Imp' then VariableValue else 0 end) AS Back_to_School_Total_FB_Imp"

Ich bin in der Lage sein, diese Teile passen, dass ich interessiert bin mit Regex in Python [example here] Aber ich würde elegant wissen, wie man (zB einer Auskleidung oder in einem Pythonic Weg) ersetzen Leerzeichen in Gruppen, die übereinstimmen.

+1

Bitte überprüfen Sie meine Antwort unten. Beachten Sie, dass, während ein Lookbehind-basierter Regex in diesem Fall optimaler ist, ich beschlossen habe, möglichst wenige Änderungen am ursprünglichen Muster selbst vorzunehmen, um zu zeigen, wie * multiple * Gruppen in einem einzigen Lambda-Ersatz behandelt werden können. –

Antwort

1

Sie benötigen einen re.sub mit einem Lambda-Ausdruck als Ersatz Argument:

import re 
s = "(case when Campaign='Back to School' and VariableName='Total_FB_Spend' then VariableValue else 0 end) AS Back to School_Total_FB_Spend,(case when Campaign='Back to School' and VariableName='Total_FB_Imp' then VariableValue else 0 end) AS Back to School_Total_FB_Imp" 
pat = r'(end\) as)([^,]*)' 
print(re.sub(pat, lambda m: "{}{}".format(m.group(1), m.group(2).replace(" ", "_")), s)) 

den Hinweis Python demo

sehen, dass ich Ihre (.*?(,|$)) mit einer effizienteren ([^,]*) ersetzt, jede null oder mehr Zeichen andere als ,. Nun, die ganze Regex Spiele:

  • (end\) as) - Gruppe 1: end) as
  • ([^,]*) - Gruppe 2: alle null oder mehr Zeichen andere als ,

Dann mit lambda m: "{}{}".format(m.group(1), m.group(2).replace(" ", "_")), der Inhalt der Die erste Erfassungsgruppe wird unverändert in das Ergebnis zurückkopiert, und die Inhalte der zweiten Gruppe werden mit .replace(" ", "_") geändert. Sicherlich können Sie einen anderen Regex darauf ausführen, wenn es einen Leerraum gibt, re.sub(r'\s+', '_', m.group(2)).

+0

Stribizew Vielen Dank für die detaillierte Erklärung, wie Stücke in Ihrer vorgeschlagenen Lösung zusammenarbeiten !! Es wirkt wie ein Zauber. Ich benutzte am Ende 're (r '\ W +', '_', m.gruppe (2))' weil es einfacher ist. Ich bin noch nicht sehr vertraut mit 'Lambda', also frage ich mich, wie es ist, Gruppen zurückzugeben, die mit dem Regex-Muster übereinstimmen (ohne dass wir etwas explizites wie 're.search (pat, s)' in der 'lambda'-Anweisung schreiben). Wenn es Ihnen nichts ausmacht, dies weiter zu erklären oder mich an eine gute Quelle zu verweisen, was das erklärt, würde ich es sehr schätzen! :) – user1330974

+0

Vielen Dank für die detaillierte Erklärung!Ihre Antworten sind sehr hilfreich und klar wegen der Patienten-/Detailerklärung. Heute habe ich gelernt, dass "re" die übereinstimmenden Objekte in "Lambda" übergibt (oder "Lambda" erfasst, was auch immer in "re" übereinstimmt). Sehr kraftvoll! – user1330974

+1

Entschuldigung für die Formatierung. Den Kommentar neu schreiben: Eigentlich steht das 'm' im Lambda für ein Matchdatenobjekt. Wenn Sie 're 'verwenden, sucht die Regex-Engine nach allen nicht überlappenden Vorkommen des Musters, und wenn eine Übereinstimmung gefunden wird, bilden alle einfangenden Gruppen' .group() 's. Also, '(a) | (b)' enthält 2 einfangende Gruppen, und wenn sie in einer 'abc' Zeichenfolge verwendet werden, wird zuerst 'a' gefunden (mit 'a' in' m.group (1) 'und null/leer (abhängig von der Python-Version) in 'm.group (2)'). Sie können auf jede Capturing-Gruppe oder das ganze Match ('m.group()') zugreifen und alles tun, was Sie wollen (in int umwandeln, inkrementieren, etc.) –

1

Eine weitere von @Wiktor inspirierte Lösung.

import re 
s = "(case when Campaign='Back to School' and VariableName='Total_FB_Spend' then VariableValue else 0 end) AS Back to School_Total_FB_Spend,(case when Campaign='Back to School' and VariableName='Total_FB_Imp' then VariableValue else 0 end) AS Back to School_Total_FB_Imp" 
pat = r'(?<=end\) as)[\w\s]*(?=,)' 
print(re.sub(pat, lambda m: m.group(0).replace(" ", "_"), s, flags=re.IGNORECASE)) 

Hier verwenden wir den Look-Ahead und Lookbehind-Funktion von Regex nur den Teil passen wir verändern wollen. Die Regex wird mit einer beliebigen Folge von Wortzeichen[a-zA-Z0-9_] und Leerzeichen gefolgt von end) as gefolgt von , übereinstimmen. Der Hauptunterschied besteht darin, dass das Match nur eine einzige Gruppe enthält.

+1

Beachten Sie, dass ein Lookbehind als Capturing Group nicht so flexibel ist wie ein Lookbehind-Muster sollte eine feste Länge haben. Sie können '\ s *' noch '\ w +' dort nicht benutzen. –

+1

In der Tat, aber da wir nach einem festen Suffix und Postfix suchen, wird dies in diesem Szenario kein Problem sein. – Jonas

+0

@Jonas Danke für den alternativen Vorschlag. Ich habe hier zum ersten Mal etwas über Lookahead/Lookbehind-Muster in Regex gelernt. Als ich den oben angegebenen Code ausprobierte, erhielt ich "IndexError: keine solche Gruppe". – user1330974

Verwandte Themen