如何在Python中根据特定条件一行代码去除文本文件中每行的变量空格?
我有一些数据(文本文件),格式乱得让人无从下手。我想尽量减少手动解析这些数据的工作量。
示例数据:
Name Degree CLASS CODE EDU Scores
--------------------------------------------------------------------------------------
John Marshall CSC 78659944 89989 BE 900
Think Code DB I10 MSC 87782 1231 MS 878
Mary 200 Jones CIVIL 98993483 32985 BE 898
John G. S Mech 7653 54 MS 65
Silent Ghost Python Ninja 788505 88448 MS Comp 887
条件:
- 多个空格应该压缩成一个分隔符(用管道符号更好?最终目标是把这些文件存入数据库)。
- 除了第一列,其他列都不会有空格,所以这些空格都可以压缩成管道符号。
- 只有第一列可以有多个单词和空格(比如:Mary K Jones)。其他列大多是数字和一些字母。
- 第一列和第二列都是字符串。它们之间几乎总是有多个空格,这样我们就能区分这两列。(如果只有一个空格,那我愿意冒这个风险,因为格式实在太糟糕了!)
- 列的数量是变化的,所以我们不需要担心列名。我们只想提取每一列的数据。
希望我说得清楚!我觉得这个任务可以用一行代码解决。我不想一直循环 :(
非常感谢“Pythonista”们读到这里,没有在这句话之前放弃!
3 个回答
这个回答是在提问者承认把数据中的每个制表符("\t")都改成了3个空格后写的(而在提问中没有提到这点)。
从第一行来看,这似乎是一个固定列宽的报告。你的数据中可能包含制表符,如果正确展开,可能会得到一个合理的结果。
与其使用 line.replace('\t', ' ' * 3)
,不如试试 line.expandtabs()
。
如果结果看起来合理(数据列对齐),你需要找出如何以编程方式确定列宽(如果可能的话)——也许可以从标题行中获取信息。
你确定第二行全是“-”吗,还是列之间有空格?问这个的原因是我曾经需要解析从数据库查询报告机制中得到的许多不同文件,这些文件的结果是这样的:
RecordType ID1 ID2 Description
----------- -------------------- ----------- ----------------------
1 12345678 123456 Widget
4 87654321 654321 Gizmoid
而且可以写一个完全通用的读取器,检查第二行来确定如何切分标题行和数据行。提示:
sizes = map(len, dash_line.split())
如果expandtabs()不起作用,请编辑你的问题,准确展示你所拥有的内容,也就是显示前5行(包括标题行)的 print repr(line)
的结果。如果你能说明是什么软件生成这些文件,那也会很有帮助。
这是对SilentGhost回答的一个变体。这次我们先把名字和其他部分分开(用两个或更多空格来分隔),然后再把其他部分分开,最后把它们合并成一个列表。
import re
for line in open(fname):
name, rest = re.split('\s{2,}', line, maxsplit=1)
print [name] + rest.split()
我觉得你的文件里可能有某种格式问题:
>>> regex = r'^(.+)\b\s{2,}\b(.+)\s+(\d+)\s+(\d+)\s+(.+)\s+(\d+)'
>>> for line in s.splitlines():
lst = [i.strip() for j in re.findall(regex, line) for i in j if j]
print(lst)
[]
[]
['John Marshall', 'CSC', '78659944', '89989', 'BE', '900']
['Think Code DB I10', 'MSC', '87782', '1231', 'MS', '878']
['Mary 200 Jones', 'CIVIL', '98993483', '32985', 'BE', '898']
['John G. S', 'Mech', '7653', '54', 'MS', '65']
['Silent Ghost', 'Python Ninja', '788505', '88448', 'MS Comp', '887']
正则表达式其实很简单,你需要注意的主要是分隔符(\s
)和在第一个分隔符情况下的单词边界(\b
)。要记住,当某一行不匹配时,你会得到一个空列表作为lst
。这就意味着需要和用户进行互动,下面会详细说明。此外,你还可以通过以下方式跳过标题行:
>>> file = open(fname)
>>> [next(file) for _ in range(2)]
>>> for line in file:
... # here empty lst indicates issues with regex
之前的版本:
>>> import re
>>> for line in open(fname):
lst = re.split(r'\s{2,}', line)
l = len(lst)
if l in (2,3):
lst[l-1:] = lst[l-1].split()
print(lst)
['Name', 'Degree', 'CLASS', 'CODE', 'EDU', 'Scores']
['--------------------------------------------------------------------------------------']
['John Marshall', 'CSC', '78659944', '89989', 'BE', '900']
['Think Code DB I10', 'MSC', '87782', '1231', 'MS', '878']
['Mary 200 Jones', 'CIVIL', '98993483', '32985', 'BE', '898']
['John G. S', 'Mech', '7653', '54', 'MS', '65']
还有一个方法就是让用户自己决定如何处理那些有问题的条目:
if l < 3:
lst = line.split()
print(lst)
iname = input('enter indexes that for elements of name: ') # use raw_input in py2k
idegr = input('enter indexes that for elements of degree: ')
嗯,我一直以为第二个元素可能包含空格,但既然不是这样,你可以直接这样做:
>>> for line in open(fname):
name, _, rest = line.partition(' ')
lst = [name] + rest.split()
print(lst)