文件对象的延迟读取以实现高效的批处理

lazyreader的Python项目详细描述


lazyreader是一个python模块,用于对文件对象进行延迟读取。

python标准库允许您一次读取一行文件,从而避免将整个文件加载到内存中。 例如:

withopen('large_file.txt')asf:forlineinf:print(line)

lazyreader允许您执行相同的操作,但是使用任意分隔符,并且对于任何呈现.read()方法的对象。 例如:

fromlazyreaderimportlazyreadwithopen('large_file.txt')asf:fordocinlazyread(f,delimiter=';'):print(doc)

这是我从Wellcome Digital Platform中派生出来的一段代码。 我们在s3中存储了大量的xml和json文件(有时是多个gbs),但每个文件实际上是一系列“文档”,由已知的分隔符分隔。 下载和解析整个文件的成本会高得吓人,但是lazyreader允许我们一次只在内存中保存一个文档。

安装

PYPI提供LazyReader:

$ pip install lazyreader

示例

如果我们有一个文件存储在本地,我们可以打开它并根据任何分隔符的选择进行拆分。 例如,如果我们有一个文本文件,其中的记录用逗号分隔:

withopen('lots_of_records.txt')asf:fordocinlazyread(f,delimiter=','):print(doc)

另一个例子:我们有一个文件存储在amazon s3中,我们希望逐行读取它。 boto3api为我们提供了一个从s3读取的文件对象:

importboto3client=boto3.client('s3')s3_object=client.get_object(Bucket='example-bucket',Key='words.txt')body=s3_object['Body']fordocinlazyread(body,delimiter=b'\n'):print(doc)

(这是最初编写此代码的用例。)

还有一个例子:我们正在获取一个html页面,并希望读取底层html中由<br>分隔的行。 喜欢这样:

importurllib.requestwithurllib.request.urlopen('https://example.org/')asf:fordocinlazyread(f,delimiter=b'<br>'):print(doc)

高级用法

lazyread()返回一个生成器,您可以包装它来构建一个生成器管道,这些生成器对数据进行处理。

第一个例子:我们有一个包含json对象列表的文件,每行一个。 (这是来自elasticdump的输出文件的格式。) 调用者真正需要的是python字典,而不是json字符串。 我们可以像这样包装lazyread()

importjsondeflazyjson(f,delimiter=b'\n'):fordocinlazyread(f,delimiter=delimiter):# Ignore empty lines, e.g. the last line in a fileifnotdoc.strip():continueyieldjson.loads(doc)

另一个例子:我们想解析一个大的XML文件,但不想一次将它全部加载到内存中。 我们可以编写以下包装:

fromlxmlimportetreedeflazyxmlstrings(f,opening_tag,closing_tag):fordocinlazyread(f,delimiter=closing_tag):ifopening_tagnotindoc:continue# We want complete XML blocks, so look for the opening tag and# just return its contentsblock=doc.split(opening_tag)[-1]yieldopening_tag+blockdeflazyxml(f,opening_tag,closing_tag):forxml_stringinlazyxmlstrings(f,opening_tag,closing_tag):yieldetree.fromstring(xml_string)

我们在wellcome使用这两个包装器来高效地处理保存在amazon s3中的大型文件。

作为一个模块,这不是有点简单吗?

也许吧。 堆栈溢出的方法与此非常相似,但我发现在独立模块中使用它很有用。

而且这并不完全是小事——至少对我来说不是。 我第一次写这篇文章时犯了两个错误:

  • 我把最初运行的字符串硬编码为

    running=b''

    这只在文件对象返回bytestrings时有效。 如果它返回unicode字符串,那么当它第一次尝试从文件中读取时,会得到一个TypeError无法将字节concat到str)。 字符串类型很重要!

  • 从文件中再读取1024个字符后,我检查了分隔符,如下所示:

    running+=new_dataifdelimiterinrunning:curr,running=running.split(delimiter)yieldcurr+delimiter

    对于我的初始用例,单个文档的比1024个字符大得多,因此新数据永远不会包含多个分隔符。 但是对于较小的文档,一次读取可能会得到多个分隔符,然后解压缩.split()的结果将抛出ValueError。 因此,现在代码正确地检查并处理单个读取包含多个分隔符的情况。

现在它已经在一个模块中进行了编码和测试,我不必担心再次犯同样的错误。

许可证

麻省理工学院。

欢迎加入QQ群-->: 979659372 Python中文网_新手群

推荐PyPI第三方库


热门话题
java如何将日期字符串解析为整数变量?   java如何在Spring3+WS2+中使用注释、JibxMarshalling或使用xml配置配置JibxMarshalling和unmarshalling?   安装Oracle Jdeveloper时mac中出现java安装错误。   设置自动保存计时器以在java中定期保存文件(但不使用任何JavaFXAPI)的最佳策略是什么?   模板获取handlebar java中迭代器列表的长度   java如何在特定JAX RS调用中排除对象中的字段?   在Struts 2中抛出NullPointerException的java getText()方法   java比较捐献者和接受者的血型   fedora Java向后兼容性   字符串Java替换多个字符   复制BuffereImage时出现java数组索引错误   并行处理Java 8并行流findFirst   java关闭应用程序后如何离开服务?   Java:Tesseractocr:如何查找单词坐标?