重新格式化 Python 中的文本
Reformatting text in Python
我是 python 的新手,在解析日志文件时遇到很大困难。你能帮我理解如何以最 Pythonic 的方式完成以下内容吗?
----- 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
所需输出 1:我想将日志重新格式化为如下所示:
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
所需输出 2:然后我希望能够查询时间戳并获得计数摘要:
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
到目前为止我的代码:
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()
有很多方法可以做到这一点。就我个人而言,我会避免为此使用正则表达式,因为它可能不会更有效并且表达式变得麻烦且不灵活。这是我想到的:
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)
输出(假定 log.log
与您提供的输入匹配)是:
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
这段代码的工作方式是使用面向对象的编程来简化我们需要做的任务:存储日志条目,以特定格式表示日志条目,并根据特定格式组合日志条目属性。
首先,注意Entry
对象及其属性(self.time
、self.animal
、self.bird
、self.fish
)表示日志中的一个条目.假设存储在其属性中的信息是正确的,可以创建一个方法将该信息表示为格式化字符串。 __repr__()
方法在 python 需要对象的字符串表示时被调用,因此它似乎是放置这段代码的好地方。该方法中大量使用了format
函数,但浏览format
.
上的python文档后应该清楚其工作原理
需要一种组合这些条目对象的方法才能获得您指定的第二个输出。这可以通过多种方式完成,而我这样做的方式不一定是最好的。我使用了 __radd__()
和 __add__()
方法,它们在对象上使用 +
运算符时被调用。通过这样做,代码 entry1 + entry2
或 sum([entry1, entry2])
可用于获取两个条目中动物的总和。然而,Entry
class 不能用于存储求和结果,因为它不能包含任意信息。相反,我选择使用 dict
对象作为两个 Entry
对象求和的结果。为了对两个以上的 Entry
对象求和,Entry
也必须能够与 dict
对象求和,因为 Entry + Entry + Entry
导致 dict + Entry
。
__add__()
函数检查它被添加到的对象是否是一个 dict
对象。如果是这种情况,它会检查条目中的每个动物是否已经存在于 dict
中。如果没有,它会将动物添加为键。否则,它将增加该键的值。 __radd__()
与__add__()
类似,只不过是在一些特殊情况下使用。有关详细信息,请参阅 python 文档。
对于对象是 Entry
的情况,可以编写代码来从每个 Entry
对象中收集所有动物并根据该信息创建一个 dict
,但是由于已经有代码可以将 Entry
添加到 dict
,因此更容易先将一个对象添加到空 dict
,然后将生成的 dict
添加到另一个对象Entry
对象。
对于所有其他对象,Entry
将简单地 return 自身的 dict
描述,或者自身添加一个空的 dict
。
现在所有的工具都可以实现前面列出的目标。要获得与所需输出 1 匹配的 Entry
的字符串表示,只需要 print entry
或 strrepr = str(entry)
。要获得所需的输出 2,需要做更多的工作,但它只是将具有相同 self.time
属性 的所有条目相加,然后显示生成的字典。
未涵盖的代码的最后一部分是解析日志以创建 Entry
对象列表。该代码只是逐行遍历日志并使用信息填充 Entry
。我觉得这很简单,但是如果没有任何意义,您可以随时提问。
我是 python 的新手,在解析日志文件时遇到很大困难。你能帮我理解如何以最 Pythonic 的方式完成以下内容吗?
----- 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
所需输出 1:我想将日志重新格式化为如下所示:
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
所需输出 2:然后我希望能够查询时间戳并获得计数摘要:
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
到目前为止我的代码:
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()
有很多方法可以做到这一点。就我个人而言,我会避免为此使用正则表达式,因为它可能不会更有效并且表达式变得麻烦且不灵活。这是我想到的:
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)
输出(假定 log.log
与您提供的输入匹配)是:
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
这段代码的工作方式是使用面向对象的编程来简化我们需要做的任务:存储日志条目,以特定格式表示日志条目,并根据特定格式组合日志条目属性。
首先,注意Entry
对象及其属性(self.time
、self.animal
、self.bird
、self.fish
)表示日志中的一个条目.假设存储在其属性中的信息是正确的,可以创建一个方法将该信息表示为格式化字符串。 __repr__()
方法在 python 需要对象的字符串表示时被调用,因此它似乎是放置这段代码的好地方。该方法中大量使用了format
函数,但浏览format
.
需要一种组合这些条目对象的方法才能获得您指定的第二个输出。这可以通过多种方式完成,而我这样做的方式不一定是最好的。我使用了 __radd__()
和 __add__()
方法,它们在对象上使用 +
运算符时被调用。通过这样做,代码 entry1 + entry2
或 sum([entry1, entry2])
可用于获取两个条目中动物的总和。然而,Entry
class 不能用于存储求和结果,因为它不能包含任意信息。相反,我选择使用 dict
对象作为两个 Entry
对象求和的结果。为了对两个以上的 Entry
对象求和,Entry
也必须能够与 dict
对象求和,因为 Entry + Entry + Entry
导致 dict + Entry
。
__add__()
函数检查它被添加到的对象是否是一个 dict
对象。如果是这种情况,它会检查条目中的每个动物是否已经存在于 dict
中。如果没有,它会将动物添加为键。否则,它将增加该键的值。 __radd__()
与__add__()
类似,只不过是在一些特殊情况下使用。有关详细信息,请参阅 python 文档。
对于对象是 Entry
的情况,可以编写代码来从每个 Entry
对象中收集所有动物并根据该信息创建一个 dict
,但是由于已经有代码可以将 Entry
添加到 dict
,因此更容易先将一个对象添加到空 dict
,然后将生成的 dict
添加到另一个对象Entry
对象。
对于所有其他对象,Entry
将简单地 return 自身的 dict
描述,或者自身添加一个空的 dict
。
现在所有的工具都可以实现前面列出的目标。要获得与所需输出 1 匹配的 Entry
的字符串表示,只需要 print entry
或 strrepr = str(entry)
。要获得所需的输出 2,需要做更多的工作,但它只是将具有相同 self.time
属性 的所有条目相加,然后显示生成的字典。
未涵盖的代码的最后一部分是解析日志以创建 Entry
对象列表。该代码只是逐行遍历日志并使用信息填充 Entry
。我觉得这很简单,但是如果没有任何意义,您可以随时提问。