2009-10-27 12 views
8

Ich habe eine Menge von Strings, die ich reduzieren muss. Sie sind im Grunde ein Deskriptor gefolgt von Codes. Ich möchte nur den Deskriptor behalten.Slice eine Zeichenfolge nach einer bestimmten Phrase?

'a descriptor dps 23 fd' 
'another 23 fd' 
'and another fd' 
'and one without a code' 

Die Codes sind über dps, 23 und fd. Sie können in beliebiger Reihenfolge auftreten, stehen nicht miteinander in Beziehung und existieren möglicherweise gar nicht (wie im letzten Fall).

Die Liste der Codes ist fest (oder kann zumindest vorhergesagt werden), also unter der Annahme, dass ein Code nie innerhalb eines legitimen Deskriptors verwendet wird, wie kann ich alles nach der ersten Instanz eines Codes entfernen.

Ich benutze Python.

+4

In Ihre Beispiele, was die Codes sind, und was sollte wie die Ausgabe aussehen? –

Antwort

21

Die kurze Antwort, wie @ THC4K Punkte in einem Kommentar aus:

string.split(pattern, 1)[0] 

wo string Ihre ursprüngliche Zeichenfolge ist, pattern ist Ihr „break“ -Muster zeigt 1 nicht mehr als 1 Mal zu spalten, und [0] bedeutet, nehmen Sie das erste Element von Split zurückgegeben.

In Aktion:

>>> s = "a descriptor 23 fd" 
>>> s.split("23", 1)[0] 
'a descriptor ' 
>>> s.split("fdasfdsafdsa", 1)[0] 
'a descriptor 23 fd' 

Dies ist ein viel kürzerer Weg auszudrücken, was ich früher geschrieben hatte, die ich hier sowieso halten.

Und wenn Sie mehrere Muster entfernen müssen, ist dies ein großer Kandidat für die reduce builtin:

>>> string = "a descriptor dps foo 23 bar fd quux" 
>>> patterns = ["dps", "23", "fd"] 
>>> reduce(lambda s, pat: s.split(pat, 1)[0], patterns, string) 
'a descriptor ' 
>>> reduce(lambda s, pat: s.split(pat, 1)[0], patterns, "uiopuiopuiopuipouiop") 
'uiopuiopuiopuipouiop' 

Diese im Grunde sagt: für jede pat in patterns: string nehmen und immer wieder string.split(pat, 1)[0] gelten (wie oben erläutert), wobei jedes Mal das Ergebnis des zuvor zurückgegebenen Wertes bearbeitet wird. Wie Sie sehen können, wenn keines der Muster in der Zeichenfolge enthalten ist, wird die ursprüngliche Zeichenfolge weiterhin zurückgegeben.


Die einfachste Antwort ist eine Liste/string Scheibe mit einem string.find kombiniert:

>>> s = "a descriptor 23 fd" 
>>> s[:s.find("fd")] 
'a descriptor 23 ' 
>>> s[:s.find("23")] 
'a descriptor ' 
>>> s[:s.find("gggfdf")] # <-- look out! last character got cut off 
'a descriptor 23 f' 

Ein besseren Ansatz (zur Vermeidung von dem letzten Zeichen in einem fehlenden Muster abschneidet, wenn s.find -1 zurück) könnte sein, in einer einfachen Funktion zu umhüllen:

>>> def cutoff(string, pattern): 
...  idx = string.find(pattern) 
...  return string[:idx if idx != -1 else len(string)] 
... 
>>> cutoff(s, "23") 
'a descriptor ' 
>>> cutoff(s, "asdfdsafdsa") 
'a descriptor 23 fd' 

[:s.find(x)] die Syntax den Teil der Zeichenfolge aus dem Index 0, bis die rechten h dauern und Seite des Doppelpunktes; In diesem Fall ist RHS das Ergebnis von s.find, das den Index der übergebenen Zeichenfolge zurückgibt.

+0

'the_string.split (Muster, 1) [0]' ist das gleiche, denke ich. –

+0

Gutes Auge, @ THC4k. –

+0

Markieren Sie mit der Teilung, was passiert, wenn es keinen Code in der Zeichenfolge gibt (eine leicht vorhandene Möglichkeit)? Und bei beiden gibt es keinen besseren Weg, mehrere Codes gleichzeitig zu prüfen. Beide Beispiele scheinen sich nur nacheinander zu befassen. – Oli

2

Sie scheinen so etwas zu werden, zu beschreiben:

def get_descriptor(text): 
    codes = ('12', 'dps', '23') 
    for c in codes: 
     try: 
      return text[:text.index(c)].rstrip() 
     except ValueError: 
      continue 

    raise ValueError("No descriptor found in `%s'" % (text)) 

Z.B.,

>>> get_descriptor('a descriptor dps 23 fd') 
'a descriptor' 
1
codes = ('12', 'dps', '23') 

def get_descriptor(text): 
    words = text.split() 
    for c in codes: 
     if c in words: 
      i = words.index(c) 
      return " ".join(words[:i]) 
    raise ValueError("No code found in `%s'" % (text)) 
1

ich wahrscheinlich einen regulären Ausdruck, dies zu tun verwenden würde:

>>> import re 
>>> descriptors = ('foo x', 'foo y', 'bar $', 'baz', 'bat') 
>>> data = ['foo x 123', 'foo y 123', 'bar $123', 'baz 123', 'bat 123', 'nothing'] 
>>> p = re.compile("(" + "|".join(map(re.escape, descriptors)) + ")") 
>>> for s in data: 
     m = re.match(p, s) 
     if m: print m.groups()[0] 
foo x 
foo y 
bar $ 
baz 
bat 

Es war nicht ganz klar zu mir, ob Sie wollen, was Sie Text enthalten sind Extrahieren, dass geht den Deskriptoren voraus oder erwartet, dass jede Textzeile mit einem Deskriptor beginnt; das oben Gesagte beschäftigt sich mit letzterem. Für erstere nur das Muster leicht ändern, um sie alle Zeichen zu machen erfassen vor dem ersten Auftreten eines Descriptor:

>>> p = re.compile("(.*(" + "|".join(map(re.escape, descriptors)) + "))") 
0

Hier ist eine Antwort, die eher für alle Codes arbeitet als Zwingen Sie die Funktion für jeden Code aufzurufen, und ist ein bisschen einfacher als einige der obigen Antworten. Es funktioniert auch für alle Ihre Beispiele.

strings = ('a descriptor dps 23 fd', 'another 23 fd', 'and another fd', 
        'and one without a code') 
codes = ('dps', '23', 'fd') 

def strip(s): 
    try: 
     return s[:min(s.find(c) for c in codes if c in s)] 
    except ValueError: 
     return s 

print map(strip, strings) 

Ausgang:

['a descriptor ', 'another ', 'and another ', 'and one without a code'] 

Ich glaube, das alle Kriterien erfüllt.

Edit: Ich erkannte schnell, konnte man den Versuch fangen entfernen, wenn Sie die Ausnahme erwarten nicht mögen:

def strip(s): 
    if not any(c in s for c in codes): 
     return s 
    return s[:min(s.find(c) for c in codes if c in s)] 
Verwandte Themen