如何读取大型csv文件的特定行

时间:2015-04-10 17:09:09

标签: python csv large-files

我正在尝试读取大型csv文件的某些特定行,我不想将整个文件加载到内存中。特定行的索引在列表L = [2, 5, 15, 98, ...]中给出,我的csv文件如下所示:

Col 1, Col 2, Col3
row11, row12, row13
row21, row22, row23
row31, row32, row33
...

使用提到的想法here我使用以下命令来读取行

with open('~/file.csv') as f:
    r = csv.DictReader(f) # I need to read it as a dictionary for my purpose

    for i in L:
        for row in enumerate(r):
            print row[i]

我立即收到以下错误:

IndexError                                Traceback (most recent call last)
<ipython-input-25-78951a0d4937> in <module>()
      6     for i in L:
      7         for row in enumerate(r):
----> 8             print row[i]
IndexError: tuple index out of range

问题1。似乎我在这里使用for循环显然是错误的。关于如何解决这个问题的任何想法?

另一方面,以下内容完成了工作,但速度太慢了:

def read_csv_line(line_number):
    with open("~/file.csv") as f:
        r = csv.DictReader(f)
        for i, line in enumerate(r):
            if i == (line_number - 2):
                return line
    return None

for i in L:
    print read_csv_line(i)

问题2。有关如何改进这种贯穿整个文件的基本方法的想法,直到我到达第i行然后打印它?

4 个答案:

答案 0 :(得分:7)

文件没有&#34;行&#34;或&#34;行&#34;。你认为是什么&#34; line&#34;是&#34;在两个换行符字符之间找到了什么&#34;。因此,如果不读取前面的行,则无法读取第n行,因为您无法计算换行符。

回答1 :如果你考虑你的例子,但是L = [9],展开你的循环会给出:

i=9
row = (0, {'Col 2': 'row12', 'Col 3': 'row13', 'Col 1': 'row11'})

如您所见,row是一个包含两个成员的元组,调用row[i]表示row[9],因此为IndexError。

回答2 :这非常慢,因为您每次都要读取文件到行号。在你的例子中,你读了前2行,然后是前5行,然后是前15行,然后是前98行等。所以你已经读了前5行3次。你可以创建一个只返回你想要的行的生成器(注意,行号将被0索引):

def read_my_lines(csv_reader, lines_list):
    for line_number, row in enumerate(csv_reader):
        if line_number in lines_list:
            yield line_number, row

因此,当你想要处理这些行时,你会这样做:

L = [2, 5, 15, 98, ...]
with open('~/file.csv') as f:
    r = csv.DictReader(f)
    for line_number, line in read_my_lines(r, L):
        do_something_with_line(line)

* 编辑 *

当您阅读了所有想要的行时,可以进一步改进以停止阅读文件:

def read_my_lines(csv_reader, lines_list):
    # make sure every line number shows up only once:
    lines_set = set(lines_list)
    for line_number, row in enumerate(csv_reader):
        if line_number in lines_set:
            yield line_number, row
            lines_set.remove(line_number)
            # Stop when the set is empty
            if not lines_set:
                raise StopIteration

答案 1 :(得分:2)

for row in enumerate(r):

将拉出元组。然后,您尝试从2元素元组中选择第i个元素。

例如

>> for i in enumerate({"a":1, "b":2}): print i
(0, 'a')
(1, 'b')

此外,由于字典是哈希表,因此不一定保留您的初始订单。例如:

>>list({"a":1, "b":2, "c":3, "d":5})
['a', 'c', 'b', 'd']

答案 2 :(得分:2)

假设L是包含所需行号的列表,您可以这样做:

with open("~/file.csv") as f:
    r = csv.DictReader(f)
    for i, line in enumerate(r):
        if i in L:    # or (i+2) in L: from your second example
            print line

那样:

  • 您只阅读了一次文件
  • 您没有将整个文件加载到内存中
  • 您只能获得您感兴趣的行

唯一需要注意的是,即使L = [3]

,您也会阅读整个文件

答案 3 :(得分:1)

总结一下好主意,我最终使用了这样的东西:L可以相对快速地排序,而在我的情况下,它实际上已经排序了。因此,不是在L中进行多次成员资格检查,而是对其进行排序,然后仅针对第一个条目检查每个索引。这是我的代码:

count=0
with open('~/file.csv') as f:
    r = csv.DictReader(f)
    for row in r:
        count += 1
        if L == []:
            break
        elif count == L[0]:
            print (row)
            L.pop(0)

请注意,只要我们完成L一次,就会停止。