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

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

今天看到了一段代码,值得记录下来。这种写法一般会写在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
相关文章
|
2月前
|
存储 安全 Java
字节流和字符流有哪些区别
字节流和字符流是Java中两种基本的I/O流类型。字节流以8位字节为单位处理数据,适用于所有类型的文件;字符流以16位Unicode字符为单位,专为文本文件设计,自动完成字符编码转换,更适合文本处理。
69 2
|
2月前
从文件中读取一行
从文件中读取一行。
30 5
|
3月前
|
Java
字节流与字符流的差异
【10月更文挑战第13天】总的来说,字节流和字符流各有其特点和适用范围,我们需要根据具体的需求来选择使用哪种类型的流。在实际应用中,还需要注意编码问题、性能优化等方面,以确保数据的正确处理和高效传输。
C++读取单个字符操作
get() 是 istream 类的成员函数,它有多种重载形式,不过本文只介绍最简单最常用的一种: int get(); 此函数从输入流中读入一个字符,返回值就是该字符的 ASCII 码。 如果碰到输入的末尾,则返回值为 EOF。EOF 是 End of File 的缩写。istream 类中从输入流(包括文件)中读取数据的成员函数,在把输入数据都读取完后再进行读取,就会返回 EOF。 EOF 是在 iostream 类中定义的一个整型常量,值为 -1。get() 函数不会跳过空格、制表符、回车等特殊字符,所有的字符都能被读入。例如下面的程序: #include <iostr
104 0
|
存储 移动开发 Java
JavaNIO实现按行读取文件操作
在Java编程中,文件操作常常是必不可少的步骤。在对文件进行操作时,按行读取文件是一个常见需求。Java提供了多种方法实现按行读取文件,其中一种方法是使用JavaNIO。
212 0
|
Java
字节流写数据加异常处理、字节流读数据、复制文本文件及复制图片
字节流写数据加异常处理、字节流读数据、复制文本文件及复制图片的简单示例
129 0
|
移动开发 Linux Windows
IO流概述分类、字节流写数据、字节流写数据的三种方式及写数据的两个小问题
IO流概述分类、字节流写数据、字节流写数据的三种方式及写数据的两个小问题的简单示例
133 0
|
存储 Java
字节缓冲流构造方法、字节流复制视频、字符流及编码表介绍
字节缓冲流构造方法、字节流复制视频、字符流及编码表介绍的简单示例
113 0
|
Java
字符串、字符流中的编码解码问题、字符流写数据的5种方式、字符流读数据的2种方式及复制Java文件
字符串、字符流中的编码解码问题、字符流写数据的5种方式、字符流读数据的2种方式及复制Java文件的简单示例
147 0
内置了一个缓冲区(数组)缓冲流BufferInputStream为何要配合字节数组的使用?
内置了一个缓冲区(数组)缓冲流BufferInputStream为何要配合字节数组的使用?
208 0