从一个字节流中进行按行读取

简介: 从一个字节流中进行按行读取

今天看到了一段代码,值得记录下来。这种写法一般会写在C语言里面做文件读写,很少会用到python之中。对于一个文件而言,如果是一个文本文件,按行读取可以直接使用python自带的方法,一般会用下面这种写法

with open(file_path, 'r') as fp:
    for line in fp:
        print(line)

在python里面可以直接对一个文件对象进行迭代,迭代的每一个结果都是文件中的每一行,这里分行是python本身来做的处理。如果是使用二进制模式打开,该怎么以流的形式读取一个文件中的每一行?

下面是一种非常优雅的写法:

# 一个使用字节模式打开的文件
fp = open(file_path, 'rb') 
def read_by_line(fp):
    # 每次读取的大小 1024字节
    BLOCK_SIZE = 1024
    # 保存最后一个换行符后面的内容
    # 原因:因为只读取了BLOCK_SIZE个大小
    # 最后一个换行符后面的内容的情况是未知的
    # 在比特流的视角下
    # xxxxxx \n xxxxxxx \n x xxxxxxxx
    # ---------------------* ————————
    #        第一次读取     
    # 每一次读取都应该将*号处(即最后一个换行符后面的数据)的数据和下次读取的数据合并起来
    # 这里 会有两种情况:
    # 1. 本次读取已经读完了所有数据
    # 2. 本次没有读取完,还有下次读取
    last_line = ''
    while True:
        data = fp.read(BLOCK_SIZE)
        # 每次读取的数据中有两种情况
        # 第一种:data种包含换行符\n,可能包含一个也可能包含多个
        # 第二种:data种不包含换行符\n
        if not data:
            # 所有数据都读取完了以后,中断循环
            break
        # 每次读取都和上一次读取的数据中最后一个换行符\n后面的数据连接起来
        data = "".join((last_line, data.decode()))
        # 将合并起来的数据再按照\n进行分割
        lines = data.split('\n')
        # 将每次读取的数据最后一个换行符后面的数据都保存起来
        # 以便于和下次的数据合并起来
        last_line = lines.pop()
        # 最后一个换行符之前的数据是没有争议的
        # 这里直接将他通过生成器返回出去
        # 当然这里还会有两种情况
        # 1. lines是一个空list, 因为前面的数据一直没有读取到换行符
        #    所以每次的数据都被保留起来,和下次的数据合并起来
        # 2. lines里面是有内容的,说明data中不止一个换行符\n
        #    这里可以将前几个换行符分割产生的行返回出去
        #    因为他们绝对不会和后面的数据有联系
        for line in lines:
            yield line
    # 所有的数据都读取完了,将最后一个换行符后的结果返回出去
    yield last_line

当然,这里如果是一个可以用文本模式直接打开的文件,最好还是使用第一种方法,第二种方法的优点在于,可以直接从一个字节流中按行进行返回。

一个很有用的场景就是,将一个文本文件使用gzip压缩以后,在不解压的前提下,可以直接在压缩的字节流上直接进行按行读取。下面是一个很有用的例子,相对于上面的代码,只需要改动很小的一部分:

# 将fp文件跟改成gzip的open
+ import gzip
+ fp = gzip.open(file_path)
+ def decompress(chunk):
+     # 解压函数
+     pass
def read_by_line(fp):
    BLOCK_SIZE = 1024
    last_line = ''
    while True:
        data = fp.read(BLOCK_SIZE)
        if not data:
            break
        # 在这里
-       data = "".join((last_line, data.decode()))
+       data = "".join(last_line, decompress(data))    #需要对读取的数据做解压
        lines = data.split('\n')
        last_line = lines.pop()
        for line in lines:
            yield line
    yield last_line
目录
打赏
0
0
0
0
33
分享
相关文章
Alibaba.com瘦包40MB——业界最全的iOS包大小技术总结
前言包大小是衡量APP性能的一项重要指标,它直接影响用户的下载点击率(包太大不想下)、下载安装成功率(下载慢不用了)、APP卸载率(太占空间先删掉)。包大小的计算逻辑很简单,它是各种类型的文件占用磁盘大小相加。APP瘦身的技术却很复杂,代码文件的复杂度和编译器策略决定了可执行文件的大小,业务功能和工程架构决定了代码文件的复杂度。iOS APP瘦身,需要掌握的技能有XCode构建技术、LLVM编译器
4250 0
Alibaba.com瘦包40MB——业界最全的iOS包大小技术总结
函数计算操作报错合集之遇到错误信息为HandlerNotFound,该怎么办
在使用函数计算服务(如阿里云函数计算)时,用户可能会遇到多种错误场景。以下是一些常见的操作报错及其可能的原因和解决方法,包括但不限于:1. 函数部署失败、2. 函数执行超时、3. 资源不足错误、4. 权限与访问错误、5. 依赖问题、6. 网络配置错误、7. 触发器配置错误、8. 日志与监控问题。
301 0
Seata常见问题之访问seata 7091端口提示报错如何解决
Seata 是一个开源的分布式事务解决方案,旨在提供高效且简单的事务协调机制,以解决微服务架构下跨服务调用(分布式场景)的一致性问题。以下是Seata常见问题的一个合集
1052 0
【日志技术】LOG4J / LOG4J2
【1月更文挑战第14天】Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J,我们可以控制日志信息输出到控 制台、文件、甚至是数据库中。我们可以控制每一条日志的输出格式,通过定义日志的输出级别,可以 更灵活的控制日志的输出过程。方便项目的调试。
shell 脚本实现 k8s 集群环境下指定 ns 资源的 yaml 文件备份
在基于 `k8s` 平台的容器化部署环境中,有时候需要快速的实现部署文件的迁移备份,当 `k8s` 平台部署一个 `app` 时,都会相应的产生一堆 `yaml` 文件,如果 `yaml` 文件数量较少,我们可以人工手动的方式进行拷贝,但是当 `yaml` 文件数量多,并且该 `k8s` 平台部署了多个 `app` 时,如果在采用...
682 0
shell 脚本实现 k8s 集群环境下指定 ns 资源的 yaml 文件备份
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等

登录插画

登录以查看您的控制台资源

管理云资源
状态一览
快捷访问