繁体   English   中英

在没有阅读行的情况下获取文本文件中的行数

[英]Getting number of lines in a text file without readlines

假设我有一个程序使用.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时间。 但是,我不确定如何获取文件中的行数(假设我找不到行数并在新用户到达时添加到行数)。 Python有没有一种方法可以在不读取整个文件并立即存储的情况下做到这一点? 我已经尝试过len() ,它显然不适用于文本文件,但是值得一试。 我考虑过的一种方法不太好,它涉及一次在范围如此之大的文本文件必须较小的范围内一次使用一行的读取行,然后在出现错误时继续执行。 我不希望使用这种方式,因此任何建议将不胜感激。

您可以直接在文件句柄上进行迭代,然后逐行对其进行迭代:

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

除此之外,如果不完全查看文件,就无法真正分辨出文件有多少行。 您确实知道文件有多大,并且可以对字符数进行一些假设(例如,UTF-8破坏了:P的含义); 但是您不知道每行有多长时间而看不到它,因此您不知道换行符在哪里,因此无法知道总共有多少行。 您仍然必须一个一个地查看每个字符,以查看是否开始新行。

因此,相反,我们只是遍历文件,并在每次读取整行时即循环主体执行时停止一次,然后继续从文件中的该位置查找下一个换行符,依此类推。

是的,好消息是您可以在文本文件中找到没有读行的行数,也可以在文件中找到行等。更具体地说,在python中,您可以使用字节函数,随机访问,并行操作和正则表达式,而不是慢速顺序文字行处理。 并行文本文件(如CSV文件行计数器)特别适合与多个处理器内核结合使用的具有快速随机访问权限的SSD设备。 我使用了16核系统和SSD,将希格斯玻色子数据集存储为标准文件,您可以下载该文件进行测试。 更具体地说,这里是工作代码的片段,可帮助您入门。 欢迎您自由复制和使用,但如果这样做,请引用我的工作谢谢:

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脚本。 在机器学习中通常需要查找数据集中的示例数量,在机器学习中,通常需要将数据集划分为训练,开发和测试示例。

希格斯玻色子数据集: https : //archive.ics.uci.edu/ml/datasets/HIGGS

如果您非常想要一个文件中的行数,为什么不使用len

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM