python - 如何以内存有效的方式逐个元素地存储读入的数据?
问题描述
我正在处理的程序需要读取 ASCII 格式的数据文件,这些文件可能非常大(最多 5GB)。格式可能会有所不同,这就是我想出 using 的原因readline()
,拆分每一行以获得纯条目,将它们全部附加到一个大的字符串列表中,然后根据某些标记词的出现将其划分为较小的字符串列表,然后将数据传递给程序内部的数据结构,以便进一步统一处理。这种方法效果很好,只是它需要大量内存,我想知道为什么。
所以我写了这个小测试用例,让你理解我的问题:这里的输入数据是莎士比亚罗密欧与朱丽叶的文本(实际上我希望混合字母 - 数字输入) - 请注意,我希望你自己复制数据以保留内容清楚地。该脚本会生成一个 .txt 文件,然后使用该文件再次读取该文件。在这种情况下,原始内存大小为153 KB。使用...读取此文件
- f.read() 也为您提供大小为153 KB的单个字符串。
- f.readlines() 为您提供一个列表,其中包含每行的单个字符串,总大小为420 KB。
- 在每个空白处拆分 f.readlines() 的行字符串并将所有这些单个条目保存在一个新列表中会导致1619 KB的内存使用。
由于这些数字在这种情况下似乎不是问题,因此对于以 GB 为顺序的输入数据,RAM 需求增加大于 10 的因子肯定是一个因子。
我不知道为什么会这样或如何避免这种情况。从我的理解来看,列表只是指向存储在列表中的所有值的指针结构(这也是为什么列表上的 sys.getsizeof() 会给出“错误”结果的原因)。对于值本身,如果我有“LONG STRING”或“LONG”+“STRING”,它不应该对内存产生影响,因为它们都使用相同的字符,这应该导致相同数量的位/字节。
也许答案真的很简单,但我真的被这个问题困住了,所以我很感谢每一个想法。
# step1: http://shakespeare.mit.edu/romeo_juliet/full.html
# step2: Ctrl+A and then Ctrl+C
# step3: Ctrl+V after benchmarkText
benchmarkText = """ >>INSERT ASCII DATA HERE<< """
#=== import modules =======================================
from pympler import asizeof
import sys
#=== open files and safe data to a structure ==============
#-- original memory size
print("\n\nAll memory sizes are in KB:\n")
print("Original string size:")
print(asizeof.asizeof(benchmarkText)/1e3)
print(sys.getsizeof(benchmarkText)/1e3)
#--- write bench mark file
with open('benchMarkText.txt', 'w') as f:
f.write(benchmarkText)
#--- read the whole file (should always be equal to original size)
with open('benchMarkText.txt', 'r') as f:
# read the whole file as one string
wholeFileString = f.read()
# check size:
print("\nSize using f.read():")
print(asizeof.asizeof(wholeFileString)/1e3)
#--- read the file in a list
listOfWordOrNumberStrings = []
with open('benchMarkText.txt', 'r') as f:
# safe every line of the file
listOfLineStrings = f.readlines()
print("\nSize using f.readlines():")
print(asizeof.asizeof(listOfLineStrings)/1e3)
# split every line into the words or punctation marks
for stringLine in listOfLineStrings:
line = stringLine[:-1] # get rid of the '\n'
# line = re.sub('"', '', line) # The final implementation will need this, however for the test case it doesn't matter.
elemsInLine = line.split()
for elem in elemsInLine:
listOfWordOrNumberStrings.append(elem)
# check size
print("\nSize after splitting:")
print(asizeof.asizeof(listOfWordOrNumberStrings)/1e3)
(我知道我在这里使用 readlines() 而不是 readline() - 我为这个测试用例更改了它,因为我认为它使事情更容易理解。)
解决方案
推荐阅读
- rust - Rust:将两个 Vec 映射到复合结构的第三个 Vec
- opencv - 如何将opencv图像与以mm为单位的尺寸相关联?
- regex - 使用 Excel VBA 提供的 RegEx 仅从 Word doc 中提取第一个匹配项
- google-apps-script - 以编程方式为容器绑定表启用特定的 appscript
- python - Django - 使用电子邮件或用户名登录不起作用
- azure-devops - 在 Azure DevOps Server 2019(本地)发布管道中,一个阶段如何将文件传递到另一个阶段?
- python-3.x - 如何使用带有 Appium 和 Python 的 Android 键盘键入文本?
- java - 这个 SpingMVC 和 Hibernate 事务有什么问题?
- angular - 如何在产品构建中包含 tsconfig.json?
- c - 当用 C 语言写在单独的行上时,反斜杠 \ 如何连接 printf 字符串?