用于解析包含表

时间:2017-04-11 02:39:26

标签: python csv

我有一个CSV文件,我需要在python中阅读和处理。

CSV文件包含以下表格值:

*aa
1    foo1    foo_bar1 
2    foo2    foo_bar2
*bb
1.22    bla1    blabla1    blablabla22
1.33    bla2    ' '        blablabla33

此处aabb是每个表的名称。无论表名出现在何处,名称前面都有*,下面的行是该表的行。

请注意,每个表都可以:

  1. 不同的列数和行数。
  2. 还可以有表示缺失值的空列。我想在阅读之后将它们保留为'。
  3. 但是,我们确切知道csv文件中存在哪些表(即表名)

    我需要读取csv文件并将表的整个内容分配给一个变量。我可以想到一种蛮力的做法。但是,由于python有一个带有读写操作的csv模块,是否有任何内置功能可以使我更容易或更有效?

    注意:到目前为止我遇到的一个主要问题是,在使用csv.reader()读取csv文件后,我看到aa的行有额外的空列。我相信这是因为aa和bb列的数量不匹配。我还想删除这些额外的空列,而不删除代表实际缺失值的空列。

3 个答案:

答案 0 :(得分:1)

最干净的方法是在将每个组提供给csv阅读器之前分离表。这是一个粗略的开始,让你开始:

from itertools import takewhile
import csv

# Instead of *s*, you can use an open file object here
s = '''\
*aa
1,foo1,foo_bar1 
2,foo2,foo_bar2
*bb
1.22,bla1,blabla1,blablabla22
1.33,bla2,       ,blablabla33
'''.splitlines()

it = iter(s)
next(it)
for table in ['aa', 'bb']:
    print(f'\nTable: {table}')
    for row in csv.reader(takewhile(lambda r: not r.startswith('*'), it)):
        print(row)

这会产生:

Table: aa
['1', 'foo1', 'foo_bar1 ']
['2', 'foo2', 'foo_bar2']

Table: bb
['1.22', 'bla1', 'blabla1', 'blablabla22']
['1.33', 'bla2', '       ', 'blablabla33']

答案 1 :(得分:0)

您是否考虑过使用熊猫?

import pandas as pd

df = pd.read_csv('foo.csv', sep=r'/s+', header=None) #if there is table headings, remove header = None

需要在文件顶部添加任何行。 这会将具有不同行数和列数的文件读入数据帧。您现在可以在其中执行各种操作。例如: 空元素由NaN表示,这意味着不是数字。您可以使用''仅写入

替换它
df.fillna(' ')

为了适合您的用例,根据我的理解,您在同一个csv文件中有多个表,请尝试:

df = pd.read_csv("foo.csv", header=None, names=range(3))
table_names = ["*aa", "*bb", "*cc"..]
groups = df[0].isin(table_names).cumsum()
tables = {g.iloc[0,0]: g.iloc[1:] for k,g in df.groupby(groups)}

这将创建一个表的列表,其中key作为表名和值,就像表本身一样。

for k,v in tables.items():
   print("table:", k)
   print(v)
   print()

您可以在documentation.

中找到更多详情

答案 2 :(得分:0)

您可以像解析csv文件那样检查第一个值是否以'*'开头并从中构建dict

import csv
from collections import defaultdict
import pprint

csv_data = defaultdict(list)
with open('data.csv', 'r') as csv_file:
    # filter empty lines
    csv_reader = csv.reader(filter(lambda l: l.strip(',\n'), csv_file))

    header = None
    for row in csv_reader:
        if row[0].startswith('*'):
            header = row[0]
        else:
            # additional row processing if needed
            csv_data[header].append(row)

pprint.pprint(csv_data)

# Output
defaultdict(<class 'list'>,
            {'*aa': [['1', ' foo1', 'foo_bar1', ''],
                     ['2', ' foo2', 'foo_bar2', '']],
             '*bb': [['1.22', ' bla1', 'blabla1', 'blablabla22'],
                     ['1.333', ' bla2', '', 'blablabla3']]})

如果由于另一个较大的元素较大而要从表中删除多余的元素,则一个选项是

csv_data[header].append(row[:col_nums[header]])

你提到的地方你知道你的桌子应该有多少列

col_nums = {'*aa' : 3, '*bb' : 4}

defaultdict(<class 'list'>,
        {'*aa': [['1', ' foo1', 'foo_bar1'], 
                 ['2', ' foo2', 'foo_bar2']],
         '*bb': [['1.22', ' bla1', 'blabla1', 'blablabla22'],
                 ['1.333', ' bla2', '', 'blablabla3']]})

如果我误读了它,你只知道列的最大数量而不是每个表的列数,那么你可以改为。

def trim_row(row):
    for i, item in enumerate(reversed(row)):
        if not item:
            break
    return row[:len(row) - i]

# use it like so
csv_data[header].append(trim_row(row))