V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
zyb201314
V2EX  ›  Python

open()读取文件问题(假设思考题).

  •  
  •   zyb201314 · 7 天前 via Android · 1104 次点击
    1, in.txt 文本文件非常非常大,(意为着不能一次读取到内存中).
    2, in.txt 文件没有换行,都在一行内,意为着直接使用 readlines 无效.
    3, in.txt 里内都是英文及标点符. 需要统计英文词频到 output.txt 文件.

    问: 下面通过迭代器 iter()函数封装的代码,能否解决,上面内容不足问题? 如不能,该如何处理?若可以, 该如何验证,内存使用量. 请大佬指教,谢谢!!
    代码如下:

    from collections import defaultdict
    import re

    a=iter(re.split('[^\w]+',open("in.txt","r").read()))
    #此条是否可避免使用内存过大?

    d=defaultdict(int)
    for b in a:
    d[b]+=1

    out=dict(sorted(d.items(),key=lambda x:x[1],reverse=True))

    f=open("output.txt","w")
    f.write(repr(out))
    f.close()
    11 条回复    2021-01-20 09:46:00 +08:00
    ysc3839
        1
    ysc3839   7 天前 via Android
    不能,因为 f.read() 就会把整个文件读到内存。
    fuis
        2
    fuis   7 天前
    我看到 split 和 read 就不看了
    zyb201314
        3
    zyb201314   7 天前 via Android
    @ysc3839 谢回复
    liprais
        4
    liprais   7 天前 via iPhone
    mmap 完事
    xpresslink
        5
    xpresslink   7 天前
    建议分块读。
    FILE_PATH = "/path/to/your/in.txt"
    CHUNK_SIZE=1024*1024*64 #64M 块
    def read_in_chunks(filePath, chunk_size):
    □□□□file_object = open(filePath,'r',encoding='utf-8')
    □□□□while True:
    □□□□□□□□chunk_data = file_object.read(chunk_size)
    □□□□□□□□if not chunk_data:
    □□□□□□□□□□□□break
    □□□□□□□□yield chunk_data


    for chunk in read_in_chunks(FILE_PATH, CHUNK_SIZE):
    □□□□handling_func(chunk)

    有很少部分单词被块截断,如果要求非常高精确,自己再处理一下。
    neoblackcap
        6
    neoblackcap   7 天前   ❤️ 1
    自己写一个简单 parser 不就可以了?实际上内存使用量可以是一个最大单词长度的两倍(动态扩展)
    这个问题不是跟处理 TCP 流一样吗?
    你只要读一定量的数据进你分配的缓冲区(预分配),然后开始解析。
    1.如果整个缓冲区都读完了但是还是未能解析出一个单词( token )那么就将缓冲区变为已有的两倍,继续读取内容并重复此步骤。
    2.如果解析到缓冲区末端,但是不是结束符(我假定是空格符),那么就继续读取你缓冲区长度减去未解析字符长度的字符。
    然后继续上面的流程,进行解析。但是有直到读到文件底部就可以了。
    autoxbc
        7
    autoxbc   7 天前
    考虑一下换语言? ry 大神新作 Deno,有个现成的函数直接对应你的需求

    https://deno.land/[email protected]/io#readstringdelim
    ----------
    readStringDelim

    Read reader[like file] chunk by chunk, splitting based on delimiter.

    import { readStringDelim } from "https://deno.land/[email protected]/io/mod.ts";
    import * as path from "https://deno.land/[email protected]/path/mod.ts";

    const filename = path.join(Deno.cwd(), "std/io/README.md");
    let fileReader = await Deno.open(filename);

    for await (let line of readStringDelim(fileReader, "\n")) {
    console.log(line);
    }

    Output:

    # std/io

    ## readLines

    ```ts
    import * as path from "https://deno.land/[email protected]/path/mod.ts";

    ## Rest of the file
    zyb201314
        8
    zyb201314   6 天前 via Android
    先谢过各大佬!

    @liprais
    稍后去了解.

    @autoxbc
    Python 学了近半年,算是新人吧,还没有精力了解别的语言哟~
    @neoblackcap

    学习了. 本人很菜,不知理解对不对.关于第 2 点,解析到缓冲区末端,如果不是符号,是否大概率英文词分两截?

    @xpresslink
    辛苦! 我想这种处理方案,应该是比较优解和大众吧.
    Latin
        9
    Latin   6 天前
    with open('filename') as file:
    for line in file:
    do_things(line)

    不换行也试试这个
    neoblackcap
        10
    neoblackcap   6 天前   ❤️ 1
    @zyb201314 有两种可能,一种是一个单词很长,你还没读完(截断了),第二种是你有可能读到单词最末尾的字符(刚刚好),两种情况都需要再读数据再解析
    AoEiuV020
        11
    AoEiuV020   20 小时 45 分钟前
    这种问题就明显感觉 java 系更合适了,java 自带的输入流就有各种各样的封装,python 只有整行读取,
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1226 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 16ms · UTC 22:31 · PVG 06:31 · LAX 14:31 · JFK 17:31
    ♥ Do have faith in what you're doing.