简短版本 如何使用Ruby在保持高性能的同时从char读取STDIN(或文件)char? (虽然问题可能不是特定于Ruby的)
长版: 在学习Ruby的同时,我设计了一个必须从管道文本数据中读取的小实用程序,在其中查找并收集数字并进行一些处理。
cat huge_text_file.txt | program.rb
input > 123123sdas234sdsd5a ...
output > 123123, 234, 5, ...
文本输入可能很大(千兆字节),它可能不包含换行符或空格(任何非数字字符都是分隔符)所以我通过字符阅读做了一个字符(尽管我对性能有所顾虑)并且它结果这样做非常慢。
只需通过char读取char而不处理900Kb输入文件大约需要7秒钟!
while c = STDIN.read(1)
end
如果我用换行符输入数据并逐行读取,则读取同一文件的速度要快100倍。
while s = STDIN.gets
end
似乎从STDIN.read(1)
的管道中读取并不涉及任何缓冲,每次读取都会发生硬盘驱动器 - 但不应该被操作系统缓存吗?
没有STDIN.gets
在内部使用char读取char,直到遇到' \n
'?
使用C,我可能会以块的形式读取数据,但我不得不处理由缓冲区窗口分割的数字,但这看起来不像Ruby的优雅解决方案。那么这样做的正确方法是什么?
P.S Timing在Python中读取同一个文件:
for line in f:
line
f.close()
运行时间为0.01秒。
c = f.read(1)
while c:
c = f.read(1)
f.close()
运行时间为0.17秒。
谢谢!
答案 0 :(得分:3)
此脚本逐字读取IO对象,并在每次找到1000个单词或到达文件末尾时执行该块。
同时在内存中保存不超过1000个单词。请注意,使用" "
作为分隔符意味着“字”可能包含换行符。
此脚本使用IO#each
指定分隔符(在本例中为空格,以获取Enumerator个单词),lazy
以避免对整个文件内容执行任何操作, each_slice
获取一系列batch_size单词。
batch_size = 1000
STDIN.each(" ").lazy.each_slice(batch_size) do |batch|
# batch is an Array of batch_size words
end
您也可以直接阅读该文件,而不是使用cat和|
:
batch_size = 1000
File.open('huge_text_file.txt').each(" ").lazy.each_slice(batch_size) do |batch|
# batch is an Array of batch_size words
end
使用此代码,不会分割任何数字,不需要逻辑,它应该比通过char读取文件char快得多,并且它将比将整个文件读入String中使用更少的内存。