使用Python解析文本文件

4 投票
6 回答
37543 浏览
提问于 2025-04-16 01:19

我刚接触Python,想用它来解析一个文本文件。这个文件大约有250到300行,格式如下:

---- Mark Grey (mark.grey@gmail.com) changed status from Busy to Available @ 14/07/2010 16:32:36 ----
----  Silvia Pablo (spablo@gmail.com) became Available @ 14/07/2010 16:32:39 ----

我需要把这个文件中的所有条目里的以下信息存储到另一个文件中(可以是Excel或文本文件)

UserName/ID  Previous Status New Status Date Time

所以我希望结果文件对于上面的条目看起来是这样的

Mark Grey/mark.grey@gmail.com  Busy Available 14/07/2010 16:32:36
Silvia Pablo/spablo@gmail.com  NaN  Available 14/07/2010 16:32:39

提前谢谢大家,

任何帮助都非常感谢

6 个回答

6

我们关注的两个正则表达式模式是……:

p1 = r'^---- ([^(]+) \(([^)]+)\) changed status from (\w+) to (\w+) (\S+) (\S+) ----$'
p2 = r'^---- ([^(]+) \(([^)]+)\) became (\w+) (\S+) (\S+) ----$'

所以我会这样做:

import csv, re, sys

# assign p1, p2 as above (or enhance them, etc etc)

r1 = re.compile(p1)
r2 = re.compile(p2)
data = []

with open('somefile.txt') as f:
    for line in f:
        m = p1.match(line)
        if m:
            data.append(m.groups())
            continue
        m = p2.match(line)
        if not m:
            print>>sys.stderr, "No match for line: %r" % line
            continue
        listofgroups = m.groups()
        listofgroups.insert(2, 'NaN')
        data.append(listofgroups)

with open('result.csv', 'w') as f:
    w = csv.writer(f)
    w.writerow('UserName/ID Previous Status New Status Date Time'.split())
    w.writerows(data)

如果我描述的这两个模式不够通用,当然可以进行一些调整,但我觉得这种大体的方法会很有用。虽然很多在Stack Overflow上的Python用户对正则表达式非常反感,但我觉得它们在处理一些临时文本时非常实用。

也许这种反感是因为有些人想用正则表达式来做一些不合适的事情,比如临时解析CSV、HTML、XML等——这些其实都有很好的解析工具可以使用!还有一些任务超出了正则表达式的“舒适区”,需要用到更强大的解析系统,比如pyparsing。另外,还有一些超级简单的任务,用简单的字符串就能很好地完成(比如我记得最近有个问题用if re.search('something', s):,而用if 'something' in s:就能解决了!)

但是对于那些相对广泛的任务(排除最简单的情况和解析结构化或稍微复杂的语法),使用正则表达式其实没有什么问题,我建议所有程序员至少要学会正则表达式的基础知识。

6
import re

pat = re.compile(r"----\s+(.*?) \((.*?)\) (?:changed status from (\w+) to|became) (\w+) @ (.*?) ----\s*")
with open("data.txt") as f:
    for line in f:
        (name, email, prev, curr, date) = pat.match(line).groups()
        print "{0}/{1}  {2} {3} {4}".format(name, email, prev or "NaN", curr, date)

这段话是说,它假设了空格的使用方式,并且认为每一行都符合某种特定的格式。如果你想要更好地处理那些不太干净的输入数据,可能需要加一些错误检查,比如检查一下 pat.match() 的结果是不是 None

16

为了帮助你入门:

result = []
regex = re.compile(
    r"""^-*\s+
    (?P<name>.*?)\s+
    \((?P<email>.*?)\)\s+
    (?:changed\s+status\s+from\s+(?P<previous>.*?)\s+to|became)\s+
    (?P<new>.*?)\s+@\s+
    (?P<date>\S+)\s+
    (?P<time>\S+)\s+
    -*$""", re.VERBOSE)
with open("inputfile") as f:
    for line in f:
        match = regex.match(line)
        if match:
            result.append([
                match.group("name"),
                match.group("email"),
                match.group("previous")
                # etc.
            ])
        else:
            # Match attempt failed

这段代码会给你一个匹配结果的数组,里面包含了各个部分。接下来,我建议你使用csv模块,把结果存储成一个标准格式。

撰写回答