【问题标题】:Best way to access the Nth line of csv file访问第 N 行 csv 文件的最佳方法
【发布时间】:2015-02-03 02:54:31
【问题描述】:

我必须访问 CSV 文件中的第 N 行。

这就是我所做的:

import csv

the_file = open('path', 'r')
reader = csv.reader(the_file)

N = input('What line do you need? > ')
i = 0

for row in reader:
    if i == N:
        print("This is the line.")
        print(row)
        break

    i += 1

the_file.close()

...但这感觉不是最理想的。精确编辑:如果文件很大,我不想遍历所有行,也不想将整个文件加载到内存中。

我确实希望像 reader[N] 这样的东西存在,但我还没有找到。

编辑答案:这条线(来自选择的答案)是我正在寻找的:

next(itertools.islice(csv.reader(f), N, None)

【问题讨论】:

  • 在代码紧凑性方面是否“最佳”?可以lines = [row for row in reader],然后lines[N]。请注意,与其他一些答案一样,这需要阅读整个文件。
  • 这个问题似乎离题了,因为它是关于工作代码的优化,最适合 //codereview.stackexchange.com
  • 正如 Ollie 所说,无论代码是什么样子,您都是从文档的位置 0 开始,然后到位置 x。这不像一个数组,可以通过一些数学运算快速跳转到正确的位置。
  • @OllieFord lines = list(reader) 会更惯用。
  • @Veedrac 不错。如果目标是代码的紧凑性,list(reader)[N] 可能是最佳选择。

标签: python file csv python-3.x


【解决方案1】:

您可以使用enumerate 遍历列表,直到找到正确的行:

for i, row in enumerate(reader):
    if i == line_number:
        print("This is the line.")
        print(row)
        break

您还可以使用专为此类场景设计的itertools.islice - 访问可迭代的特定切片而不将整个内容读入内存。它应该比遍历不需要的行更有效。

def get_csv_line(path, line_number):
    with open(path) as f:
        return next(itertools.islice(csv.reader(f), line_number, None))

但是,如果您的 CSV 文件很小,只需将整个内容读入一个列表,然后您就可以使用索引以正常方式访问该列表。这还有一个好处是您可以以随机顺序访问多个不同的行,而无需重置 csv 阅读器。

with open(path) as f:
    my_csv_data = list(csv.reader(f))
print(my_csv_data[line_number])

【讨论】:

  • 要使用enumerate() 正确获取行号,您可能需要将关键字参数start=1 添加到调用中。
  • @martineau 也许可以,但我保留它以匹配从 0 开始的 OP 代码。
  • pythons csv 阅读器有一个 line_num 属性,所以如果你不想,你就不必使用 enumerate。类似...如果 reader.line_num == N:做某事
  • @NaveenReddyMarthala 查看我的编辑,它将代码放在函数中。您每次都需要重新打开文件;使用with 块最容易做到这一点。如果您正在阅读很多不同的行,那么将整个 CSV 文件读入一个列表可能是最简单的,就像这个答案中的第 3 个解决方案一样。
  • @naveenreddymarthala 是不是太大了?您可以在第二个解决方案中使用该函数,或者对其进行调整以获取行号列表而不是一个,或者使用某种缓存系统
【解决方案2】:

您的解决方案实际上并没有那么糟糕。将文件迭代器推进到您想要的行是一种很好的方法,并且在许多这样的情况下使用。

如果你想让它更简洁,你可以使用 nextenumerategenerator expression

import csv

the_file = open('path', 'r')
reader = csv.reader(the_file)

N = int(input('What line do you need? > '))

line = next((x for i, x in enumerate(reader) if i == N), None)
print(line)

the_file.close()

其中的None 是如果找不到该行将返回的内容(N 太大)。不过,您可以选择任何其他值。


您也可以使用with-statement 打开文件以使其自动关闭:

import csv

with open('path', 'r') as the_file:
    reader = csv.reader(the_file)

    N = int(input('What line do you need? > '))

    line = next((x for i, x in enumerate(reader) if i == N), None)
    print(line)

如果你真的想缩小尺寸,你可以这样做:

from csv import reader
N = int(input('What line do you need? > '))
with open('path') as f:
    print(next((x for i, x in enumerate(reader(f)) if i == N), None))

【讨论】:

  • next 很好用!以前从未见过:-) +1
【解决方案3】:

itertools 模块有许多用于创建专用迭代器的函数——它的islice() 函数可以用来轻松解决这个问题:

import csv
import itertools

N = 5  # desired line number

with open('path.csv', newline='') as the_file:
    row = next(csv.reader(itertools.islice(the_file, N, N+1)))

print("This is the line.")
print(row)

附:出于好奇,我最初的反应——也有效(可以说更好)——是:

    row = next(itertools.islice(csv.reader(the_file), N, N+1))

【讨论】:

  • 如果您确定 CSV 将源代码行 1:1 映射到 CSV 行,请先对文件 (islice(infile, ...)) 进行切片,然后再将其传递到 csv.reader
  • @Veedrac 除了你没有从中获得任何好处,除了以后可能出现错误数据......我个人会坚持使用原始版本
  • @JonClements 我的意思是将其发布为您需要更快访问速度的替代方案;它不保证是正确的,所以我也不会默认使用它。
  • @JonClements 对于N = 16...4096,我的方法的速度优势是[2, 3, 5, 7, 10, 12, 12, 15, 16] 的两倍。
  • @Veedrac 我会花一点时间 - 但肯定不会想到有那么大的区别!非常感谢您花时间回复一些数字。
【解决方案4】:

你可以这样做:

n = 2 # line to print
fd = open('foo.csv', 'r')
lines = fd.readlines()
print lines[n-1] # prints 2nd line
fd.close()

或者通过不将整个文件加载到内存中来更好地利用更少的内存:

import linecache
n = 2
linecache.getline('foo.csv', n)

【讨论】:

  • @OllieFord:感谢您的观察。 linecache 可以作为替代使用。
  • 我不知道linecache - 这似乎是一个很好的解决方案!
  • CSV 中的多行字段怎么样?
  • 据我了解,CSV 格式的文档允许在字段中换行,只要它们在双引号内(第 2 节,第 6 段),所以如果有这种可能性,最好使用 @987654326 @
  • 如果您查看linecachemodule 的源代码,您会发现它也使用file.readlines() 将整个文件读入内存,因此它不会使用更少的内存。它还产生了额外的开销,因为它旨在用于从 [其他] 导入的模块中读取多行,而不是从数据文件中读取单行。
【解决方案5】:

您可以将for 循环最小化为理解表达式,例如

row = [row for i,row in enumerate(reader) if i == N][0]  

# or even nicer as seen in iCodez code with next and generator expression

row = next(row for i,row in enumerate(reader) if i == N)

【讨论】:

    【解决方案6】:
    import csv
    with open('cvs_file.csv', 'r') as inFile: 
        reader = csv.reader(inFile)
        my_content = list(reader)
    
    line_no = input('What line do you need(line number begins from 0)? > ')
    if line_no < len(my_content):
        print(my_content[line_no])
    else:
        print('This line does not exists')
    

    作为result,现在您可以通过index directly 获取任何线路:

    What line do you need? > 2
    ['101', '0.19', '1']
    
    What line do you need? > 100
    This line does not exists
    

    【讨论】:

    • @Veedrac 谢谢,我使用 dict 基于 d[k] 的 O(1) 复杂度,但即使 l[i] 也具有相同的 O(1) 复杂度。
    猜你喜欢
    • 2018-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-01
    • 2013-05-27
    • 1970-01-01
    相关资源
    最近更新 更多