Python - 读取文件并按分隔符分割行的最佳方法

2 投票
2 回答
10130 浏览
提问于 2025-04-17 04:14

读取文件的最佳方法是什么?并且要根据某个分隔符将行分开。返回的数据应该是一个元组的列表。

这个方法还有更好的替代方案吗?有没有更快或者更省内存的方法?

def readfile(filepath, delim):
    with open(filepath, 'r') as f:
        return [tuple(line.split(delim)) for line in f]

2 个回答

4

使用生成器代替列表,使用列表代替元组,可以减少内存的使用,这样就不需要一次性把整个文件都读到内存里。

def readfile(path, delim):
    return (ln.split(delim) for ln in open(f, 'r'))

不过,你得依靠垃圾回收器来关闭文件。至于返回元组:如果没有必要的话就别这么做,因为列表的速度稍微快一点,构建元组的成本很小,而且(更重要的是)你的行会被分割成可变大小的序列,这在概念上就是列表。

要提高速度,可能只能深入到C/Cython的层面;str.split的性能很难超越,因为它是用C写的,而列表推导式在Python中是我所知道的最快的循环结构。

更重要的是,这段代码非常清晰且符合Python的风格。我不会尝试对这段代码进行优化,除了生成器的部分。

15

你发的代码会把整个文件读进来,然后在内存里创建一个文件内容的副本,所有内容都分成一个个元组,每行一个元组。既然你想要减少内存的使用,其实你只需要一个生成器函数:

def readfile(filepath, delim): 
    with open(filepath, 'r') as f: 
        for line in f:
            yield tuple(line.split(delim))

但是!这里有个大问题!你只能遍历一次生成的元组。

lines_as_tuples = readfile(mydata,','):

for linedata in lines_as_tuples:
    # do something

到这里为止,这没什么问题,生成器和列表看起来是一样的。但假设你的文件里有很多浮点数,而你在遍历文件的时候要计算这些数的平均值。你可以用“# do something”这段代码来计算总和和数字的个数,然后算出平均值。但如果你想再遍历一次,这次是找出每个值与平均值的差,你可能会想再加一个for循环:

for linedata in lines_as_tuples:
    # do another thing
    # BUT - this loop never does anything because lines_as_tuples has been consumed!

哇!这就是生成器和列表之间的一个大区别。到这段代码的时候,生成器已经被完全用完了——但并不会抛出什么特别的异常,for循环就会默默地什么都不做,继续往下执行!

在很多情况下,你得到的列表只会遍历一次,这样把readfile转换成生成器是没问题的。但如果你想要一个更持久的列表,需要多次访问,那么仅仅使用生成器就会出问题,因为你只能遍历生成器一次。

我的建议是?把readlines做成一个生成器,这样它在自己的小世界里,每次只返回文件的一小部分,既节省内存又高效。如果调用者需要多次引用返回的数据,那就让调用者自己从生成器构建一个列表——在Python中可以很简单地用 list(readfile('file.dat', ',')) 来实现。

撰写回答