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

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 ADB设备不显示手机、og droid   java使用存储和保存方法写入属性文件   java为什么要打印这个结果   JavaSpring自动连线和并行流   java如何在Hashmap中存储KStream对象类型的值?   Android中Telugu字符串的java正则表达式?   java平方求幂   Eclipse和Tomcat的java错误   Java数组不继续循环   Jsoup未解析java嵌套html   java Android:如何知道MediaPlayer是否暂停?   跟踪Java POJO类中字段的空值集的最佳方法   java Spring在应用程序属性相对与绝对中配置工作目录?   向固定大小的平面添加随机放置的直线矩形的java方法   JavaEclipse和jasper报告:如何定位log4j。财产?