2009-10-27 9 views
8

Mam partię strun, które muszę wyciąć. Są w zasadzie deskryptorem, po którym następują kody. Chcę tylko zachować deskryptor.Cięcie łańcucha po określonej frazie?

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

Kody powyżej są dps, 23 i fd. Mogą przyjść w dowolnej kolejności, nie są ze sobą powiązane i mogą w ogóle nie istnieć (jak w ostatnim przypadku).

Lista kodów jest stała (lub przynajmniej można ją przewidzieć), więc zakładając, że kod nie jest nigdy używany w uzasadnionym deskryptorze, jak mogę usunąć wszystko po pierwszym wystąpieniu kodu.

Używam Pythona.

+4

W swoich przykładach, jakie są kody, a co powinno wyglądać wyjście? –

Odpowiedz

21

Krótka odpowiedź, jak @ THC4K zwraca uwagę w komentarzu:

string.split(pattern, 1)[0] 

gdzie string jest oryginalny łańcuch, pattern jest Twój „break” wzór, 1 wskazuje podzielić nie więcej niż 1 godzinę, a [0] oznacza pierwszy element zwracany przez dzielenie.

w akcji:

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

ten jest znacznie krótszy sposób wyrażania tego, co napisałem wcześniej, które będę tutaj tak.

A jeśli trzeba usunąć wiele wzorów, jest to świetny kandydat na reduce wbudowanego polecenia:

>>> 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' 

To w zasadzie mówi: dla każdego pat w patterns: wziąć string i wielokrotnie zastosować string.split(pat, 1)[0] (jak wyjaśniono powyżej), operując na wynikach poprzednio zwróconej wartości za każdym razem. Jak widać, jeśli żaden z wzorów nie znajduje się w łańcuchu, pierwotny ciąg jest nadal zwracany.


Najprostsza odpowiedź jest kawałek lista/string połączeniu z string.find:

>>> 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' 

Lepszym rozwiązaniem (aby uniknąć odcinając ostatni znak w brakującej wzór kiedy s.find zwraca -1) może być zawijany w prostej zależności:

>>> 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' 

Składnia [:s.find(x)] oznacza się część łańcucha z indeksem 0 do prawej godz i bok jelita; i w tym przypadku RHS jest wynikiem s.find, który zwraca indeks ciągu, który przekazałeś.

+0

'the_string.split (pattern, 1) [0]' to to samo myślę. –

+0

Dobre oko, @ THC4k. –

+0

Zaznacz przy podziale, co się stanie, jeśli nie ma kodu w ciągu (nieznacznie, choć istniejąca możliwość)? A w przypadku obu nie ma lepszego sposobu sprawdzania wielu kodów naraz. Oba przykłady wydają się dotyczyć tylko jednego na raz. – Oli

2

Wydaje się, że opisując coś takiego:

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)) 

np,

>>> 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

pewnie bym użyć wyrażenia regularnego, aby to zrobić:

>>> 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 

To nie było do końca jasne dla mnie, czy chcesz, co masz do wydobycia to tekst, który poprzedza deskryptory lub jeśli spodziewasz się, że każda linia tekstu zacznie się od deskryptora; powyższe dotyczy tego drugiego. W pierwszym przypadku wystarczy zmienić wzorzec lekko, aby uchwycić wszystkie znaki przed pierwszym wystąpieniem Eurovoc:

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

Oto odpowiedź, która działa dla wszystkich kodów zamiast zmuszając do wywołania funkcji dla każdego kodu, i jest nieco prostsze niż niektóre z powyższych odpowiedzi. Działa również dla wszystkich twoich przykładów.

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) 

wyjściowa:

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

Wierzę, że ta spełnia wszystkich kryteriów.

Edit: I zrealizowane szybko można usunąć blokadę spróbować, jeśli nie podoba spodziewa wyjątek:

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)] 
Powiązane problemy