在python中有效地解析CSV

时间:2017-08-25 08:58:32

标签: python-2.7 csv parsing optimization

我正在编写一个具有以下结构

的CSV解析器
class decode:
def __init__(self):
    self.fd = open('test.csv')


def decodeoperation(self):
    for row in self.fd:
        getcmd = self.decodecmd(row)
        if cmd == 'A'
            self.decodeAopt()

        elif cmd == 'B':
            self.decodeBopt()

def decodeAopt(self):
    for row in self.fd:
        #decodefurther dependencies based on cmd A till 
        #a condition occurs on any further row

    return  
def decodeBopt(self):
    for row in self.fd:
        #decodefurther dependencies based on cmd B till 
        #a condition occurs on any further row
    return

当前代码对我来说工作正常,但在所有方法中迭代CSV文件我感觉不太好。它能以更好的方式完成吗?

1 个答案:

答案 0 :(得分:2)

在多个方法中使用公共迭代器没有任何本质上的错误,只要您可以提前确定在序列中的任何给定点分配哪个方法(您通过解码行中的cmd来执行此操作)得到'A','B'等等。如果您在确定要调用哪种方法之前必须阅读多个项目,则设计会出现问题,如果您选择了错误的方法并且需要尝试另一种方法,则可能需要备份。在解析中,这称为回溯。由于您传递文件对象,因此很难进行备份。请注意,您的单独解码器方法必须知道何时在读取包含命令的下一行之前停止,因此它们需要某种可以识别的终止哨兵行。

关于Python和类设计的一些一般性评论:

你有一个很好的简单if-elif-elif调度表,它可以转换成这样的Python字典:

# put this code in place of your "if cmd == ... elif elif elif..." code
dispatch = {
    # note - no ()'s, we just want to reference the methods, not call them
    'A': self.decodeAopt,  
    'B': self.decodeBopt,
    'C': self.decodeCopt,
    # look how easy it is to add more decoders
}

# lookup which decoder to use for the current cmd
decoder = dispatch[cmd]

# run it
decoder()

# or do it all in one line
dispatch[cmd]()

不要让__init__方法打开文件,而是让它接受迭代器对象。这样可以更轻松地为对象编写测试,因为您将能够传递包含CSV行的简单Python列表。

class decode:
    def __init__(self, sequence):
        self.fd = sequence

您可能希望将此变量从'fd'重命名为'seq',因为它不必是文件,但可以是任何可解码行的迭代。

如果您正在进行自己的CSV解析,请查看使用内置csv模块。它将为您完成相当多的工作,例如解析可能包含逗号的引用字符串,并且可以为每行提供易于使用的dicts,给定从输入文件读取的标头,或者由您指定。如果您按照我的建议修改了__init__,则可以使用它:

import csv

# assuming test.csv has a header row
reader = csv.DictReader(open('test.csv'))

# or specify headers if not - I encourage you to give these columns better names
reader.fieldnames = ['cmd', 'val1', 'val2', 'val3']

decoder = decode(reader)
decoder.decodeoperation()

然后你可以用decodeoperation编写:

cmd = row['cmd']

请注意,这会给你的类带来稍微不同的设计,它会期望得到一系列的dicts,而不是一串字符串。