【问题标题】:Converting space delimited text file using python?使用python转换空格分隔的文本文件?
【发布时间】:2018-08-27 19:54:11
【问题描述】:

我正在尝试将此以空格分隔的文本文件转换为列和行(我希望最终将其转换为 JSON)。我的脚本可能无法分隔列,主要是因为我正在寻找空格。我无法更改输入的格式(进入的文本文件)

我的文件格式有问题,这是一个非常简单的例子

col1 col2                 col3 text                      col4

1403 bash                 2014-07-28 22:32:53 UTC+0000   sudo bash
1464 bash                 2014-07-28 22:32:28 UTC+0000   sudo root

当我解析文件时,我得到了破折号下方的数据:

['1403', 'bash', '2014-07-28', '22:32:53', 'UTC+0000', 'sudo', 'bash']

我希望它看起来像这样:

['1403', 'bash', '2014-07-28 22:32:53 UTC+0000', 'sudo bash']

这是一个非常基本的例子。但基本上我将标题映射到破折号下方的数据。让我知道您是否可以提供任何帮助。

注意:输出不必完全如图所示,我只是希望能够分离 cols 数据。

到目前为止,这是我在代码中的内容,它将标题分离到各个列:

colNames = testFile.readline()
#tempList = re.split('(\s )', colNames)
headerList = []
for i in tempList:
    j = i.replace(' ','')
    if j != '':
        headerList.append(j)

然后我有一个循环来根据数据的位置遍历数据(这是我认为我需要找到一种更好地分离数据的方法):

   for line in testFile.readlines():
        if rowCounter > 0:
            row = line.split()
            print row
            for i in range(len(headerList)):
                colNameDic.update({headerList[i]:row[i]})
            rowDic = dict(colNameDic)
            fileList.append(rowDic)
            rowDic = {}
        rowCounter +=1

【问题讨论】:

  • 您解析文件不正确。您需要一个固定列解析器,例如pandas.read_fwf()
  • 您需要发布您的代码。而且,正如前面评论中所说,它不是一个以空格分隔的文件。它是 1,然后是 15(?),然后是 3 个空格
  • @ChristopherSchneider 我尝试关注空间差异,但有些文件有很多空间,而另一些则没有。例如,如果您注意到 col1 数据和 col2 数据之间只有一次空格,但两者都是不同的 cols。有意义吗?
  • @DYZ 我会检查一下。刚刚发布代码(对不起,这是我的第一次)
  • 您确定它是col3 text 而不是,例如col3_text 带有下划线、点或逗号...因为空间与标题的结构不同从数据行的结构来看

标签: python json text type-conversion whitespace


【解决方案1】:

如果标题行的列名中没有多余的空格,则可以使用slice 内置函数拆分数据行(请注意,我在标题行中添加了下划线规范化列的名称)

In [20]: h = 'col1 col2                 col3_text                      col4\n'
In [21]: r = '1403 bash                 2014-07-28 22:32:53 UTC+0000   sudo bash\n'
In [22]: fields = h.split()
    ...: ifields = [h.index(f) for f in fields]+[None]
In [23]: slices = [slice(i,j) for i, j in zip(ifields, ifields[1:])]
In [24]: tokens = [r[s] for s in slices]
In [25]: tokens
Out[25]: 
['1403 ',
 'bash                 ',
 '2014-07-28 22:32:53 UTC+0000   ',
 'sudo bash\n']
In [26]: 

PS 你可能想rstrip 个别项目,[r[s].rstrip() for s in slices]


附录

如果可以在有效列名和垃圾之间找到判别式,则可以放宽无多余空格的要求...重新使用您的特定数据格式(无下划线...)

In [35]: h = 'col1 col2                 col3 text                      col4\n'
In [36]: r = '1403 bash                 2014-07-28 22:32:53 UTC+0000   sudo bash\n'
In [37]: fields = [f for f in h.split() if f[:3]=='col']
In [38]: ifields = [h.index(f) for f in fields]+[None]
In [39]: slices = [slice(i,j) for i, j in zip(ifields, ifields[1:])]
In [40]: tokens = [r[s].rstrip() for s in slices]
In [41]: tokens
Out[41]: ['1403', 'bash', '2014-07-28 22:32:53 UTC+0000', 'sudo bash']
In [42]: 

【讨论】:

  • 是的,我已经弄清楚了标题,主要是困扰我的行。我要测试一下。
  • 这里我使用了一个简单的完美匹配,你可以使用正则表达式、验证函数,任何你认为合适的东西
  • 快速问题标记提取所有行正确吗?
  • 如果字段/标记在每一行中具有相同的长度,是的。 OTOH,如果数据编写器选择溢出而不是截断,那么您的船就在糟糕的水域中......
  • 我在上一条评论中的意思是,看看字段#1,它看起来像一个 PID,不是吗?如果 PID 是 5 位数会怎样?它仍然可以,但如果它是 6 位数字(如果我的记忆准确,PID 不可能,这只是一个例子)你可能有麻烦了......
【解决方案2】:

只要列宽固定,就可以使用定宽解析器:

pd.read_fwf(io.StringIO(data), skiprows=[1], 
            parse_dates=["col3 text"], # Optional
            colspecs=[(0,4),(5,25),(26,54),(57,66)])
#   col1  col2            col3 text       col4
#0  1403  bash  2014-07-28 22:32:53  sudo bash
#1  1464  bash  2014-07-28 22:32:28  sudo root

【讨论】:

  • 是的,宽度不固定。谢谢你。这仍然有帮助!
  • 如果你没有固定的列并且没有一个分隔符,那么你唯一的希望就是写一个描述一行的正则表达式。
【解决方案3】:

这是另一种方法:对文本行执行两次处理。在第一遍中,我们将猜测起始索引:非空格字符前面有空格的索引。对于每一行,起始索引可能略有不同,但如果我们查看它们全部共有的那些索引,那么这些索引可能是列的开始。起始索引的猜测并不完美。它要求一行中没有一个单元格丢失

在第二遍中,我们将使用这些索引将一行分成几列,去掉尾随的空格。

import itertools

def split_columns(row, start_indices):
    start_indices = start_indices + [None]
    a, b = iter(start_indices), iter(start_indices)
    next(b)

    columns = []
    for istart, istop in zip(a, b):
        columns.append(row[slice(istart, istop)].strip())

    return columns

def guess_start_indices(row):
    row = ' ' + row
    prev_seq, cur_seq = iter(row), iter(row)
    next(cur_seq)

    start_indices = []
    for i, (prev, cur) in enumerate(zip(prev_seq, cur_seq)):
        if prev == ' ' and cur != ' ':
            start_indices.append(i)
    return set(start_indices)

def find_common_start_indices(rows):
    start_indices = set.intersection(*(guess_start_indices(row) for row in rows))
    start_indices = sorted(start_indices)
    return start_indices

if __name__ == '__main__':
    with open('columnize.txt') as rows:
        first_pass, second_pass = itertools.tee(rows)

        start_indices = find_common_start_indices(first_pass)
        print(start_indices)

        for row in second_pass:
            print(split_columns(row, start_indices))

注意事项

  • 在代码中,我创建了两个迭代器first_passsecond_pass 来帮助迭代文本行。这些迭代器很重要,因为它们允许在不倒回文件的情况下对行进行两次迭代。
  • 这种方法的主题是猜测,所以会有文字欺骗编码,它会做出错误的猜测
  • 因此,使用此解决方案作为起点并验证输出

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-11-14
    • 2014-05-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多