3

我有一些数据(文本文件)以人们能想到的最不均匀的方式格式化。我试图尽量减少解析这些数据的手动工作量。

样本数据 :

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)。其余的列主要是数字和一些字母。
  • 第一列和第二列都是字符串。它们之间几乎总是有多个空格,因此我们可以区分两列。(如果有一个空格,考虑到可怕的格式,这是我愿意承担的风险!)。
  • 列数各不相同,因此我们不必担心列名。我们想要的只是提取每一列的数据。

希望我说得通!我有一种感觉,这个任务可以在一个单行机中完成。我不想循环,循环,循环:(

Muchos gracias “Pythonistas” 一路阅读,在这句话之前没有退出!

4

3 回答 3

3

您的文件中似乎仍然存在某种格式:

>>> 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)
于 2010-10-06T15:30:15.933 回答
2

SilentGhost 答案的变体,这次首先将名称与其余名称分开(由两个或更多空格分隔),然后仅拆分其余部分,最后制作一个列表。

import re

for line in open(fname):
    name, rest = re.split('\s{2,}', line, maxsplit=1)
    print [name] + rest.split()
于 2010-10-06T16:19:04.300 回答
1

这个答案是在 OP 承认将他的数据中的每个选项卡(“\t”)更改为 3 个空格(并且在他的问题中没有提到它)之后写的。

看第一行,似乎这是一个固定列宽的报告。您的数据完全有可能包含选项卡,如果正确扩展这些选项卡可能会导致非疯狂结果。

而不是line.replace('\t', ' ' * 3)尝试line.expandtabs()

expandtabs 的文档在这里

如果结果看起来合理(数据列对齐),您将需要确定如何以编程方式计算列宽(如果可能的话)——也许从标题行开始。

你确定第二行都是“-”,还是列之间有空格? 询问的原因是,我曾经需要从数据库查询报告机制中解析许多不同的文件,结果如下所示:

RecordType  ID1                  ID2         Description           
----------- -------------------- ----------- ----------------------
1           12345678             123456      Widget                
4           87654321             654321      Gizmoid

并且可以编写一个完全通用的阅读器,检查第二行以确定在哪里分割标题线和数据线。暗示:

sizes = map(len, dash_line.split())

如果 expandtabs() 不起作用,请编辑您的问题以准确显示您所拥有的内容,即显示print repr(line)前 5 行左右的结果(包括标题行)。如果您能说出生成这些文件的软件可能也很有用。

于 2010-10-07T20:43:12.333 回答