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

How to read specific lines of a large csv file

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

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

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

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 行然后打印的基本方法有什么想法吗?

for row in enumerate(r):

将拉取元组。然后,您正尝试 select 来自 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']

假设 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]

也需要读取整个文件

一个文件没有 "lines" 或 "rows"。您认为 "line" 是 "what is found between two newline characters"。因此,如果不阅读它之前的行,您将无法阅读第 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 次。您可以创建一个仅 returns 您想要的行的生成器(注意,行号将从 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

只是为了总结伟大的想法,我最终使用了这样的东西: 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 一次,它就会停止。