2016-07-12 12 views
0

Ich bin neu in Python und mit großen Schwierigkeiten beim Parsen durch eine Protokolldatei. Kannst du mir bitte helfen, zu verstehen, wie ich das Unten auf die pythischste Art erreichen kann?Text in Python neu formatieren

----- Log Entry 5 ----- 
Time  : 2016-07-12 09:00:00 
Animal  : Brown Bear 
Bird  : White Owl 
Fish  : Salmon 


----- Log Entry 6 ----- 
Time  : 2016-07-12 09:00:00 
Animal  : Brown Bear 
Bird  : Parrot 
Fish  : Tuna 


----- Log Entry 7 ----- 
Time  : 2016-07-12 09:00:00 
Animal  : Lion 
Bird  : White Owl 
Fish  : Sword Fish 


----- Log Entry 8 ----- 
Time  : 2016-07-12 09:15:00 
Animal  : Lion 
Bird  : White Owl 
Fish  : Sword Fish 

Wunsch Ausgang 1: Ich möchte das Protokoll neu zu formatieren, wie die unten aussehen:

Time: 2016-07-12 09:00:00 Animal: Brown Bear Bird: White Owl Fish : Salmon 
Time: 2016-07-12 09:00:00 Animal: Brown Bear Bird: Parrot  Fish : Tuna 
Time: 2016-07-12 09:00:00 Animal: Lion  Bird: White Owl Fish : Sword Fish 
Time: 2016-07-12 09:15:00 Animal: Lion  Bird: White Owl Fish : Sword Fish 

Wunsch Ausgang 2: Dann würde ich gerne die Möglichkeit haben, einen Zeitstempel abfragen und erhalten eine Zusammenfassung der Zählungen: So Far

Time: 2016-07-12 09:00:00 
Name:  Count: 
Brown Bear 2 
Lion  1 
White Owl 2 
Parrot  1 
Salmon  1 
Tuna  1 
Sword Fish 1 

Time: 2016-07-12 09:15:00 
Name:  Count: 
Lion  1 
White Owl 1 
Sword Fish 1 

Mein Code:

import os, sys, time, re, collections, subprocess 

show_cmd = 'cat question | egrep -v \'^$|=|Log\' | awk \'ORS=NR%4?FS:RS\' | grep Time' 
log = (subprocess.check_output(show_cmd, shell=True).decode('utf-8')) 

def time_field(): 
    logRegex = re.compile(r'Time\s*:.*\d\d\d-\d\d-\d\d\s\d\d:\d\d') 
    log_parsed = (logRegex.findall(log)) 
    a = (str(log_parsed).replace(' ', '')) 
    a = ((' ' + a[1:-1]).split(',')) 
    for i in a: 
     print(i) 

time_field() 
+4

Was haben Sie bisher versucht? Mit welchen Schwierigkeiten sind Sie in Ihrer aktuellen Implementierung konfrontiert? Können Sie bitte einen [mcve] Ihres letzten Codeversuchs bereitstellen? – idjaw

+0

Kannst du die Datei irgendwie lesen? – depperm

+0

zeigt Ihnen die erwartete Ausgabe oder soll das im Datenformat gespiegelt werden? – patrick

Antwort

1

Es gibt viele Möglichkeiten, dies zu tun. Persönlich würde ich vermeiden, Regex dafür zu verwenden, weil es wahrscheinlich nicht effizienter sein wird und der Ausdruck umständlich und unflexibel wird. Hier ist etwas, was ich kam mit:

class Entry: 
    def __init__(self): 
     self.time = None 
     self.animal = None 
     self.bird = None 
     self.fish = None 

    def __repr__(self): 
     fmt = "{0} {1} {2} {3}".format(
      "Time: {time: <{width}}", 
      "Animal: {animal: <{width}}", 
      "Bird: {bird: <{width}}", 
      "Fish: {fish: <{width}}") 
     return fmt.format(
      time=self.time, animal=self.animal, 
      bird=self.bird, fish=self.fish, 
      width=12) 

    def __radd__(self, other): 
      return self.__add__(other) 

    def __add__(self, other): 
     if type(other) == dict: 
      for i in [self.animal, self.bird, self.fish]: 
       if i in other: other[i] += 1 
       else: other[i] = 1 
      return other 
     elif type(other) == Entry: 
      return self.__add__({}) + other 
     else: 
      return self.__add__({}) 

def parse_log(path): 
    def extract(line): 
     start = line.find(':') + 1 
     return line[start:].strip() 

    entries = [] 
    entry = None 
    with open(path, 'r') as f: 
     for line in f.readlines(): 
      if line.startswith('-----'): 
       if entry: entries.append(entry) 
       entry = Entry() 
      elif line.startswith('Time'): 
       entry.time = extract(line) 
      elif line.startswith('Animal'): 
       entry.animal = extract(line) 
      elif line.startswith('Bird'): 
       entry.bird = extract(line) 
      elif line.startswith('Fish'): 
       entry.fish = extract(line) 

     if entry: entries.append(entry) 

    return entries 


def print_output_1(entries): 
    for entry in entries: 
     print entry 

def print_output_2(entries, time): 
    animals = sum([e for e in entries if e.time == time]) 

    print "Time: {0}".format(time) 
    print "Name:  Count:" 
    for animal, count in animals.items(): 
     print "{animal: <{width}} {count}".format(
       animal=animal, count=count, width=12) 


logPath = 'log.log' 
time = '2016-07-12 09:15:00' 
entries = parse_log(logPath) 

print_output_1(entries) 
print "" 
print_output_2(entries, time) 

Der Ausgang (vorausgesetzt, dass log.log wird die Eingangs Sie gab) ist:

Time: 2016-07-12 09:00:00 Animal: Brown Bear Bird: White Owl Fish: Salmon 
Time: 2016-07-12 09:00:00 Animal: Brown Bear Bird: Parrot  Fish: Tuna 
Time: 2016-07-12 09:00:00 Animal: Lion   Bird: White Owl Fish: Sword Fish 
Time: 2016-07-12 09:15:00 Animal: Lion   Bird: White Owl Fish: Sword Fish 

Time: 2016-07-12 09:15:00 
Name:  Count: 
White Owl 1 
Sword Fish 1 
Lion   1 

Die Art und Weise dieser Code funktioniert, ist die objektorientierte Programmierung zu unserem Vorteil zu nutzen Um die Aufgaben zu vereinfachen, die wir tun müssen: Protokolleinträge speichern, Protokolleinträge in einem bestimmten Format darstellen und Protokolleinträge gemäß einer bestimmten Eigenschaft kombinieren.

Beachten Sie zunächst, dass das Entry Objekt und seine Eigenschaften (self.time, self.animal, self.bird, self.fish) im Protokoll einen Eintrag darstellt. Unter der Annahme, dass die in den Eigenschaften gespeicherten Informationen korrekt sind, kann eine Methode erstellt werden, um diese Informationen als formatierte Zeichenfolge darzustellen. Die Methode __repr__() wird aufgerufen, wenn Python die Zeichenfolgendarstellung eines Objekts wünscht, also schien es ein guter Platz, um diesen Code zu platzieren. In dieser Methode wird die format-Funktion häufig verwendet, aber es sollte klar sein, wie sie nach dem Durchsuchen der Python-Dokumentation unter format funktioniert.

Es wird eine Methode zum Kombinieren dieser Eintragsobjekte benötigt, um die von Ihnen angegebene zweite Ausgabe zu erhalten. Dies kann auf viele Arten geschehen und die Art und Weise, wie ich es gemacht habe, ist nicht unbedingt das Beste. Ich habe die Methoden __radd__() und __add__() verwendet, die aufgerufen werden, wenn der Operator + für ein Objekt verwendet wird. Auf diese Weise kann der Code entry1 + entry2 oder sum([entry1, entry2]) verwendet werden, um die Summe der Tiere in beiden Einträgen zu erhalten. Die Klasse Entry kann jedoch nicht zum Speichern des Ergebnisses der Summe verwendet werden, da sie keine beliebigen Informationen enthalten kann. Stattdessen wählte ich ein Objekt dict, um das Ergebnis der Summierung zwei Entry Objekte zu sein. Um mehr als zwei Entry Objekte zu summieren, muss Entry auch in der Lage sein, mit einem dict Objekt zu summieren, weil Entry + Entry + Entrydict + Entry ergibt.

Die Funktion __add__() prüft, ob das Objekt, zu dem es hinzugefügt wird, ein Objekt dict ist. Wenn dies der Fall ist, prüft es, ob jedes der Tiere in dem Eintrag bereits in der dict existiert. Wenn nicht, wird das Tier als Schlüssel hinzugefügt. Andernfalls wird der Wert dieses Schlüssels erhöht. __radd__() ist ähnlich wie __add__(), außer dass es unter bestimmten Umständen verwendet wird. Weitere Informationen finden Sie in der Python-Dokumentation.

Für den Fall, dass das Objekt ein Entry, Code geschrieben worden sein könnte von jedem Entry Objekt alle Tiere zu sammeln und ein dict aus dieser Information erstellen, aber da gibt es bereits Code ist ein Entry mit einem dict hinzufügen Es ist einfacher, zuerst ein Objekt zu einem leeren dict hinzuzufügen und dann das resultierende dict mit dem anderen Entry Objekt hinzuzufügen.

Für alle anderen Objekte, Entry wird einfach die dict Beschreibung von sich selbst zurückgeben, oder sich selbst mit einem leeren dict hinzugefügt.

Jetzt sind alle Tools vorhanden, um die oben aufgeführten Ziele zu erreichen. Um eine Zeichenfolgendarstellung von Entry zu erhalten, die mit der gewünschten Ausgabe 1 übereinstimmt, ist nur print entry oder strrepr = str(entry) erforderlich. Um die gewünschte Ausgabe 2 zu erhalten, ist ein wenig mehr Arbeit erforderlich, aber es werden einfach alle Einträge summiert, die die gleiche self.time-Eigenschaft haben, und dann wird das resultierende Diktat angezeigt.

Der letzte Teil des Codes, der nicht behandelt wird, ist das Parsen des Protokolls, um eine Liste von Entry Objekten zu erstellen. Der Code läuft zeilenweise durch das Protokoll und füllt eine Entry mit den Informationen. Ich habe das Gefühl, dass das ziemlich einfach ist, aber Sie können gerne Fragen stellen, wenn es keinen Sinn ergibt.

+0

Dies erreicht genau das, was ich gesucht habe! Um ehrlich zu sein, muss ich etwas Zeit investieren, um Ihre Methode zu verstehen. Ich bin ein Neuling bei Python, aber das ist eine gute Referenz für mich, da ich mit vielen Logs in diesem Format zu tun habe. Vielen Dank für Ihre schnelle Antwort und Unterstützung! – MBasith

+0

Ich habe eine Erklärung des Codes hinzugefügt, die es hoffentlich klarer macht. Wenn diese Antwort Ihren Anforderungen entspricht, können Sie sie als korrekt markieren. – Alden

+0

Danke Alden für die Erklärung. Das war sehr hilfreich! – MBasith