2016-05-06 22 views
1

aus einem String wieSpiel erste Klammer mit Python

70849 mozilla/5.0(linux;u;android4.2.1;zh-cn)applewebkit/534.30(khtml,likegecko)version/4.0mobilesafari/534.30 

Ich mag linux;u;android4.2.1;zh-cn den ersten geklammerten Inhalt erhalten.

Mein Code sieht wie folgt aus:

s=r'70849 mozilla/5.0(linux;u;android4.2.1;zh-cn)applewebkit/534.30(khtml,likegecko)version/4.0mobilesafari/534.30' 
re.search("(\d+)\s.+\((\S+)\)", s).group(2) 

aber das Ergebnis ist die letzte Klammer Inhalt khtml,likegecko.

Wie löst man das?

Antwort

1

Das Hauptproblem Sie haben, ist die gierig Punkt .+ Muster entsprechen. Es nimmt die gesamte Zeichenfolge, die Sie haben, und dann Backtracks, wobei Sie jeweils ein Zeichen von rechts nach rechts ziehen und versuchen, die nachfolgenden Muster zu berücksichtigen. Somit entspricht es den letzten Klammern.

können Sie verwenden

^(\d+)\s[^(]+\(([^()]+)\) 

die regex demo See. Hier beschränkt die [^(]+ den Abgleich auf andere Zeichen als ( (also kann nicht die gesamte Zeile bis zum Ende gepackt werden) und gelangt zum ersten Klammerpaar.

Muster expalantion:

  • ^ - String Start (HINWEIS: Wenn die Zahl nicht zu Beginn der Zeichenfolge angezeigt wird, entfernen Sie diese ^ Anker)
  • (\d+) - Gruppe 1: 1 oder mehr
  • Ziffern
  • \s - ein Leerzeichen (wenn es kein erforderliches Zeichen ist, kann es, da die nachfolgenden negierte Zeichenklasse entfernt werden passt auf t er space)
  • [^(]+ - 1+ andere Zeichen als (
  • \( - Ein Literal (
  • ([^()]+) - Gruppe 2 passende 1+ andere Zeichen als ( und )
  • \) - Schließung ).

Regular expression visualization

Debuggex Demo

Hier ist die IDEONE demo:

import re 
p = re.compile(r'^(\d+)\s[^(]+\(([^()]+)\)') 
test_str = "70849 mozilla/5.0(linux;u;android4.2.1;zh-cn)applewebkit/534.30(khtml,likegecko)version/4.0mobilesafari/534.30" 
print(p.findall(test_str)) 
# or using re.search if the number is not at the beginning of the string 
m = re.search(r'(\d+)\s[^(]+\(([^()]+)\)', test_str) 
if m: 
    print("Number: {0}\nString: {1}".format(m.group(1), m.group(2))) 
# [('70849', 'linux;u;android4.2.1;zh-cn')] 
# Number: 70849 
# String: linux;u;android4.2.1;zh-cn 
+0

Es funktioniert! Mein '(\ d +) \ s \ (([^()] *) \ s \ (([^()] *) \) 'fehlgeschlagen liefert null @Wiktor –

+0

Bitte beachten Sie meine Erklärungen: Ihr Muster enthielt ein gieriges Punkt-Matching-Muster, das die ersten Klammern übersprang, um zu den letzten zu gelangen statt dessen. –

+0

Ich weiß, gierigen Auswirkungen! '(\ d +) \ s (([^()] *))' Dies gab nicht die Nummer, verwenden Sie die Gruppe (1). Aber Ihre funktioniert –

1

Sie können eine negierte Klasse verwenden \(([^)]*)\) etwas zwischen ( und ) zum Spiel:

>>> s=r'70849 mozilla/5.0(linux;u;android4.2.1;zh-cn)applewebkit/534.30(khtml,likegecko)version/4.0mobilesafari/534.30' 

>>> m = re.search(r"(\d+)[^(]*\(([^)]*)\)", s) 
>>> print m.group(1) 
70849 
>>> print m.group(2) 
linux;u;android4.2.1;zh-cn 
+1

Sie müssen wollten sagen * negiert Zeichenklasse * ('[^)]') anstelle einer * Negation Regex *.Ich denke, 'r '\ (([^()] *) \)'' ist jedoch sicherer, da es kein "wildes" '(' zwischen dem ersten '(' 'und dem nächsten' ')' ' –

+0

einfangen wird @anubhava Wenn ich die erste Nummer bekommen will 70849 '(\ d +) \ s \ (([^()] *) \)' benutze group (1) richtig? –

+1

Nein, das ist nicht korrekt.Es gibt kein Untermuster von '\ s' zu' ('. –