2013-05-21 22 views
5

Ich habe viele Protokolldateien und möchten einige Muster mit Multiline suchen, aber um übereinstimmende Zeichenfolge leicht zu finden, möchte ich immer noch die Linie zu sehen Nummer für übereinstimmenden Bereich.Python regex, in Multiline übereinstimmen, aber immer noch die Zeilennummer

Jeder gute Vorschlag. (Codebeispiel kopiert wird)

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] die lineno ist die tatsächliche Zeilennummer

So ist der Ausgang I sieht wie wünschen:

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

Suchen Sie nach tatsächlichen Zeilennummern oder nach den Zahlen, die Sie nach dem '####' geschrieben haben? – interjay

+0

danke, ich brauche die tatsächlichen Zeilennummern, die Probe ist irreführend, ich aktualisierte es. –

Antwort

3

Sie können die Zeilennummern nur vorher speichern und danach suchen.

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

in der Nähe zu arbeiten, können Sie überprüfen, meine aktualisierte Frage und Beispielergebnis –

+0

es soll Gruppe 1 sein, dann Code 'print 'Zeile Nr.% D:% s'% (nächste (i für i im Bereich (len (Zeile)) if line [i]> m.start (1)), m.gruppe (1)) ', bitte aktualisiere den Code –

5

Was Sie wollen, ein typisch ist Aufgabe, dass Regex nicht sehr gut ist; Parsing.

Sie könnten die Protokolldatei Zeile für Zeile lesen und diese Zeile nach den Zeichenfolgen durchsuchen, die Sie verwenden, um Ihre Suche zu begrenzen. Sie könnten Regex Zeile für Zeile verwenden, aber es ist weniger effizient als reguläre Zeichenfolgenabgleich, wenn Sie nicht nach komplizierten Mustern suchen. Wenn Sie sind auf der Suche nach komplizierten Übereinstimmungen, würde ich es gerne sehen. Die Suche nach jeder Zeile in einer Datei für #### unter Beibehaltung der Zeilenanzahl ist ohne Regex einfacher.

+0

das ist ganz vernünftig, und ich möchte sehen, ob es eine elegante Lösung dafür gibt. –

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

Ergebnis

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

danke, aber die Lineno, die ich will, ist die echte Zeilennummer –

0

Die finditer Funktion können Sie den Bereich Zeichen sagen, dass abgestimmt. Von diesem können Sie einen einfachen Newline-regulären Ausdruck verwenden, um zu zählen, wie viele Zeilen vor dem Match neu waren. eine der Anzahl der Zeilenumbrüche hinzufügen manipuliert Text in einem Editor die Zeilennummer, wie unsere Konvention zu erhalten, ist die erste Zeile zu nennen 1 statt 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)) 

gibt diese Ausgabe

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

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

Dies kann ziemlich effizient erfolgen:

  • Finding alle
  • Spiele über newlines Looping, Speichern des {offset: line_number} mappin g bis zum letzten Spiel.
  • Suchen Sie für jedes Spiel rückwärts den Offset des ersten Zeilenvorschubs und suchen Sie nach der Zeilennummer in der Karte.

Dies vermeidet das Zurückzählen für jede Übereinstimmung bis zum Anfang der Datei.

Die folgende Funktion ist ähnlich 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) 

Wenn Sie den Inhalt möchten, können Sie die letzte Schleife mit ersetzen:

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) 

Beachten Sie, dass es schön wäre, um zu vermeiden, zu schaffen eine Liste von finditer, aber das bedeutet, dass wir nicht wissen, wann die Speicherung von Zeilenumbrüchen (wo es am Ende viele neue Zeilen speichern könnte, auch wenn das einzige Muster Übereinstimmung am Anfang der Datei ist).

Wenn es wichtig war, alle Übereinstimmungen zu vermeiden, ist es möglich, einen Iterator zu erstellen, der Zeilenumbrüche nach Bedarf scannt, obwohl dies nicht sicher ist, dass Sie in der Praxis viel davon profitieren würden.

0

Ich glaube, das tut mehr oder weniger, was Sie wollen:

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

Es könnte ein wenig langsamer als andere Optionen, weil es immer wieder einen Teil Spiel tut und auf der Saite suchen, aber da die Zeichenfolge ist klein in Ihrem Beispiel, ich denke, es lohnt sich der Kompromiss aus Gründen der Einfachheit.

Was wir hier gewinnen, ist auch der Bereich von Linien, die mit dem Muster übereinstimmen, was es uns ermöglicht, die gesamte Zeichenfolge auf einen Schlag zu extrahieren. Wir können dies weiter optimieren, indem wir die Anzahl der Zeilenumbrüche im Spiel zählen, anstatt für das, was es wert ist, direkt zum Ende zu gehen.

Verwandte Themen