2013-05-21 12 views
5

Mam wiele plików dziennika i chcę wyszukać niektóre wzorce za pomocą multilinii, ale aby łatwo znaleźć dopasowany ciąg, nadal chcę zobaczyć linię numer dla dopasowanego obszaru.python regex, mecz w multilinie, ale nadal chcesz uzyskać numer wiersza

Każda dobra sugestia. (Przykładowy kod jest kopiowany)

string=""" 
####1 
ttteest 
####1 
ttttteeeestt 

####2 

ttest 
####2 
""" 

import re 
pattern = '.*?####(.*?)####' 
matches= re.compile(pattern, re.MULTILINE|re.DOTALL).findall(string) 
for item in matches: 
    print "lineno: ?", "matched: ", item 

[UPDATE] Przedmiotem lineno jest rzeczywista liczba linii

więc wyjście Chcę wygląda następująco:

lineno: 1, 1 
    ttteest 
    lineno: 6, 2 
    ttttteeeestt 
+0

Czy szukasz aktualnych numerów linii lub numerów, które napisałeś po "####"? – interjay

+0

dziękuję, potrzebuję rzeczywistych numerów linii, próbka wprowadza w błąd, zaktualizowałem ją. –

Odpowiedz

3

Numery linii można zapisać tylko ręcznie, a następnie wyszukać.

import re 

string=""" 
####1 
ttteest 
####1 
ttttteeeestt 

####2 

ttest 
####2 
""" 

end='.*\n' 
line=[] 
for m in re.finditer(end, string): 
    line.append(m.end()) 

pattern = '.*?####(.*?)####' 
match=re.compile(pattern, re.MULTILINE|re.DOTALL) 
for m in re.finditer(match, string): 
    print 'lineno :%d, %s' %(next(i for i in range(len(line)) if line[i]>m.start(1)), m.group(1)) 
+0

blisko do pracy, czy możesz sprawdzić moje zaktualizowane pytanie i przykładowy wynik –

+0

to będzie grupa 1, następnie kod 'drukuj 'linia nr% d:% s'% (następna (i dla i w zakresie (len (linia)), jeśli line [i]> m.start (1)), m.group (1)) ', proszę zaktualizować kod –

5

Co chcesz to typowy zadanie, które regex nie jest zbyt dobre; rozbiór gramatyczny zdania.

Można odczytywać plik dziennika po linii i wyszukiwać w wierszu dla ciągów, których używasz do ograniczenia wyszukiwania. Możesz używać linii regex po linii, ale jest ona mniej wydajna niż regularne dopasowywanie ciągów, chyba że szukasz skomplikowanych wzorów.

Jeśli jesteś , jesteś szukając skomplikowanych dopasowań, chciałbym to zobaczyć. Przeszukiwanie każdej linii w pliku dla #### przy zachowaniu liczby wierszy jest łatwiejsze bez wyrażeń regularnych.

+0

jest to całkiem rozsądne i chcę sprawdzić, czy istnieje do tego eleganckie rozwiązanie. –

-1
import re 

text = """ 
####1 
ttteest 
####1 
ttttteeeestt 

####2 

ttest 
####2 
""" 

pat = ('^####(\d+)' 
     '(?:[^\S\n]*\n)*' 
     '\s*(.+?)\s*\n' 
     '^####\\1(?=\D)') 
regx = re.compile(pat,re.MULTILINE) 

print '\n'.join("lineno: %s matched: %s" % t 
       for t in regx.findall(text)) 

wynik

lineno: 1 matched: ttteest 
lineno: 2 matched: ttest 
+0

dziękuję, ale potrzebuję lineno jest prawdziwym numerem linii –

0

Funkcja finditer można powiedzieć zakres znaków, który pasował. Z tego możesz użyć prostego nowego wyrażenia regularnego, aby zliczyć, ile nowych linii było przed meczem. Dodasz do liczby nowych linii, aby uzyskać numer wiersza, jako naszej konwencji w manipulowaniu tekst w edytorze jest wywołanie pierwszej linii 1 zamiast 0.

def multiline_re_with_linenumber(): 
    string=""" 
####1 
ttteest 
####1 
ttttteeeestt 

####2 

ttest 
####2 
""" 
    re_pattern = re.compile(r'.*?####(.*?)####', re.DOTALL) 
    re_newline = re.compile(r'\n') 
    count = 0 
    for m in re_pattern.finditer(string): 
     count += 1 
     start_line = len(re_newline.findall(string, 0, m.start(1)))+1 
     end_line = len(re_newline.findall(string, 0, m.end(1)))+1 
     print ('"""{}"""\nstart={}, end={}, instance={}'.format(m.group(1), start_line, end_line, count)) 

Daje to wyjście

"""1 
ttteest 
""" 
start=2, end=4, instance=1 
"""2 

ttest 
""" 
start=7, end=10, instance=2 
0

można to zrobić dość skutecznie przez:

  • Znalezienie wszystkie mecze
  • zapętlenie nad nowymi liniami, przechowywania {offset: line_number} Mappin g aż do ostatniego meczu.
  • Dla każdego meczu, znajdź wcześniej odsunięcie pierwszego nowego wiersza i wyszukaj jego numer linii na mapie.

Pozwala to uniknąć liczenia z powrotem do początku pliku dla każdego meczu.

Poniższa funkcja jest podobna do re.finditer

def finditer_with_line_numbers(pattern, string, flags=0): 
    ''' 
    A version of 're.finditer' that returns '(match, line_number)' pairs. 
    ''' 
    import re 

    matches = list(re.finditer(pattern, string, flags)) 
    if not matches: 
     return [] 

    end = matches[-1].start() 
    # -1 so a failed 'rfind' maps to the first line. 
    newline_table = {-1: 0} 
    for i, m in enumerate(re.finditer(r'\n', string), 1): 
     # don't find newlines past our last match 
     offset = m.start() 
     if offset > end: 
      break 
     newline_table[offset] = i 

    # Failing to find the newline is OK, -1 maps to 0. 
    for m in matches: 
     newline_offset = string.rfind('\n', 0, m.start()) 
     line_number = newline_table[newline_offset] 
     yield (m, line_number) 

Jeśli chcesz zawartość można zastąpić ostatnią pętlę z:

for m in matches: 
     newline_offset = string.rfind('\n', 0, m.start()) 
     newline_end = string.find('\n', m.end()) # '-1' gracefully uses the end. 
     line = string[newline_offset + 1:newline_end] 
     line_number = newline_table[newline_offset] 
     yield (m, line_number, line) 

Zauważ, że byłoby miło, aby uniknąć konieczności tworzenia lista od finditer, ale oznacza to, że nie będziemy wiedzieć, kiedy przestać zapisywać nowe linie (gdzie może to skończyć przechowywanie wielu linii, nawet jeśli jedyne dopasowanie wzorca znajduje się na początku pliku).

Jeśli ważne jest, aby unikać przechowywania wszystkich meczów - możliwe jest wykonanie iteratora, który skanuje znaki nowej linii zgodnie z potrzebą, choć nie jest pewne, czy w praktyce przyniesie to wiele korzyści.

0

wierzę to robi mniej więcej to, co chcesz:

import re 

string=""" 
####1 
ttteest 
####1 
ttttteeeestt 

####2 

ttest 
####2 
""" 

pattern = '.*?####(.*?)####' 
matches = re.compile(pattern, re.MULTILINE|re.DOTALL) 
for match in matches.finditer(string): 
    start, end = string[0:match.start()].count("\n"), string[0:match.end()].count("\n") 
    print("lineno: %d-%d matched: %s" % (start, end, match.group())) 

To może być trochę wolniej niż inne opcje, ponieważ wielokrotnie robi mecz podciąg i szukać na strunie, ale ponieważ struny jest mały w twoim przykładzie, myślę, że to jest warte kompromisu dla prostoty.

To, co tutaj zyskujemy, to także linia o zakresie linii, które pasują do wzorca, co pozwala nam wyodrębnić cały ciąg za jednym razem. Możemy zoptymalizować to dalej, licząc liczbę nowych linii w meczu, zamiast iść prosto do końca, bo to jest warte.