获取没有readlines的文本文件中的行数

时间:2014-01-17 17:28:43

标签: python python-3.x

假设我有一个程序使用.txt文件来存储运行所需的数据。因为它是一个非常大量的数据(只是随身携带)在文本文件中我使用生成器而不是迭代器来遍历其中的数据,以便我的程序留下尽可能多的空间。我们只是说(我知道这不安全)它是一个用户名列表。所以我的代码看起来像这样(使用python 3.3)。

for x in range LenOfFile:
    id = file.readlines(x)
    if username == id:
       validusername = True
       #ask for a password
if validusername == True and validpassword == True:
    pass
else:
    print("Invalid Username")

假设我要求输入密码时,有效密码设置为True或False。我的问题是,因为我不想占用所有的RAM,所以我不想使用readlines()来获取整个内容,而且这里的代码我只需要占用很少的RAM时间。但是,我不确定如何获得文件中的行数(假设我找不到行数并在新用户到达时添加到行数)。有没有一种方法可以在不读取整个文件并立即存储的情况下执行此操作?我已经尝试了len(),这显然不适用于文本文件,但值得一试。我想到这样做的一种方法并不是太大,它只涉及在一个范围内一次一行地使用readlines,文本文件必须更小,然后在我收到错误时继续。我不想这样使用,所以任何建议都会受到赞赏。

3 个答案:

答案 0 :(得分:2)

你可以直接迭代文件句柄,然后逐行迭代它:

for line in file:
    if username == line.strip():
       validusername = True
       break

除此之外,如果不完全查看文件,您无法确定文件的行数。你知道文件有多大,你可以对字符数进行一些假设(例如UTF-8废墟:P);但你不知道每一行没有看到多长时间,所以你不知道换行的位置,因此无法分辨出总共有多少行。你仍然需要逐个查看每个字符,看看是否有新行开始。

所以不是那样,我们只是迭代文件,每当我们读取整行时停止一次 - 那就是循环体执行时 - 然后我们继续从文件中的那个位置查找下一个换行符,并且等等。

答案 1 :(得分:1)

是的,好消息是你可以在没有readlines的文本文件中找到行数,for line in file等。更具体地说,在python中你可以使用字节函数,随机访问,并行操作和正则表达式来代替慢顺序文本行处理。像CSV文件行计数器这样的并行文本文件特别适用于具有快速随机访问的SSD设备,当与多个处理器内核结合使用时。我使用带有SSD的16核系统将Higgs Boson数据集存储为标准文件,您可以下载进行测试。更具体地说,这里是来自工作代码的片段,可以帮助您入门。欢迎您自由复制和使用,但如果您这样做,请引用我的工作,谢谢:

import re
from argparse import ArgumentParser
from multiprocessing import Pool
from itertools import repeat
from os import stat

unitTest = 0
fileName = None
balanceFactor = 2
numProcesses = 1

if __name__ == '__main__':
    argparser = ArgumentParser(description='Parallel text file like CSV file line counter is particularly suitable for SSD which have fast random access')
    argparser.add_argument('--unitTest', default=unitTest, type=int, required=False, help='0:False  1:True.')
    argparser.add_argument('--fileName', default=fileName, required=False, help='')
    argparser.add_argument('--balanceFactor', default=balanceFactor, type=int, required=False, help='integer: 1 or 2 or 3 are typical')
    argparser.add_argument('--numProcesses', default=numProcesses, type=int, required=False, help='integer: 1 or more. Best when matched to number of physical CPU cores.')
    cmd = vars(argparser.parse_args())
    unitTest=cmd['unitTest']
    fileName=cmd['fileName']
    balanceFactor=cmd['balanceFactor']
    numProcesses=cmd['numProcesses']

#Do arithmetic to divide partitions into startbyte, endbyte strips among workers (2 lists of int)
#Best number of strips to use is 2x to 3x number of workers, for workload balancing
#import numpy as np  # long heavy import but i love numpy syntax

    def PartitionDataToWorkers(workers, items, balanceFactor=2):
        strips = balanceFactor * workers
        step = int(round(float(items)/strips))
        startPos = list(range(1, items+1, step))
        if len(startPos) > strips:
            startPos = startPos[:-1]
        endPos = [x + step - 1 for x in startPos]
        endPos[-1] = items
        return startPos, endPos

    def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'):  # counts number of searchChar appearing in the byte range
        with open(fileName, 'r') as f:
            f.seek(startByte-1)  # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte.
            bytes = f.read(endByte - startByte + 1)
            cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times.
        return cnt

    if 0 == unitTest:
        # Run app, not unit tests.
        fileBytes = stat(fileName).st_size  # Read quickly from OS how many bytes are in a text file
        startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor)
        p = Pool(numProcesses)
        partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values.
        globalSum = sum(partialSum)
        print(globalSum)
    else: 
        print("Running unit tests") # Bash commands like: head --bytes 96 beer.csv  are how I found the correct values.
        fileName='beer.csv' # byte 98 is a newline
        assert(8==ReadFileSegment(1, 288, fileName))
        assert(1==ReadFileSegment(1, 100, fileName))
        assert(0==ReadFileSegment(1,  97, fileName))
        assert(1==ReadFileSegment(97, 98, fileName))
        assert(1==ReadFileSegment(98, 99, fileName))
        assert(0==ReadFileSegment(99, 99, fileName))
        assert(1==ReadFileSegment(98, 98, fileName))
        assert(0==ReadFileSegment(97, 97, fileName))
        print("OK")

bash wc程序稍快但你想要纯python,我也是如此。下面是一些性能测试结果。也就是说,如果你改变一些代码来使用cython或其他东西,你甚至可以获得更快的速度。

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.257s
user    0m12.088s
sys 0m20.512s

HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv
11000000 HIGGS.csv

real    0m1.820s
user    0m0.364s
sys 0m1.456s


HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000

real    0m2.256s
user    0m10.696s
sys 0m19.952s

HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1
11000000

real    0m17.380s
user    0m11.124s
sys 0m6.272s

结论:与C程序相比,纯python程序的速度很快。但是,在C程序中使用纯python程序还不够好。

我想知道是否只编译一次正则表达式并将其传递给所有工作人员将提高速度。答:正则表达式预编译对此应用程序没有帮助。我想原因是所有工人的流程序列化和创建的开销占主导地位。

还有一件事。并行CSV文件读取甚至帮助,我想知道吗?磁盘是瓶颈,还是CPU?哦,是的,是的。并行文件读取效果很好。那你去吧!

数据科学是纯python的典型用例。我喜欢使用python(jupyter)笔记本,我喜欢将所有代码保存在笔记本中,而不是尽可能使用bash脚本。查找数据集中的示例数量是进行机器学习的常见需求,您通常需要将数据集划分为训练,开发和测试示例。

Higgs Boson 数据集: https://archive.ics.uci.edu/ml/datasets/HIGGS

答案 2 :(得分:0)

如果您想要文件中的行数如此糟糕,为什么不使用len

with open("filename") as f:
    num = len(f.readlines())