为什么“吞食”文件不是一个好习惯?

2023-11-30

为什么“slurping”文件对于普通文本文件 I/O 来说不是一个好的做法,它什么时候有用?

例如,为什么我不应该使用这些?

File.read('/path/to/text.txt').lines.each do |line|
  # do something with a line
end

or

File.readlines('/path/to/text.txt').each do |line|
  # do something with a line
end

我们一次又一次地看到询问如何读取文本文件以逐行处理它的问题,这些问题使用以下变体read, or readlines,一次将整个文件拉入内存。

的文档read says:

打开文件,可选择查找给定的偏移量,然后返回长度字节(默认为文件的其余部分)。 [...]

的文档readlines says:

将按名称指定的整个文件读取为单独的行,并以数组形式返回这些行。 [...]

拉入小文件没什么大不了的,但是随着传入数据缓冲区的增长,内存必须被重新洗牌,这会消耗 CPU 时间。此外,如果数据消耗太多空间,操作系统就必须介入才能保持脚本运行并开始假脱机到磁盘,这将使程序崩溃。在 HTTPd(网络主机)或需要快速响应的东西上,它会削弱整个应用程序。

Slurping 通常是基于对文件 I/O 速度的误解,或者认为先读取然后分割缓冲区比一次读取一行更好。

这里有一些测试代码来演示“slurping”引起的问题。

将其保存为“test.sh”:

echo Building test files...

yes "abcdefghijklmnopqrstuvwxyz 123456890" | head -c 1000       > kb.txt
yes "abcdefghijklmnopqrstuvwxyz 123456890" | head -c 1000000    > mb.txt
yes "abcdefghijklmnopqrstuvwxyz 123456890" | head -c 1000000000 > gb1.txt
cat gb1.txt gb1.txt > gb2.txt
cat gb1.txt gb2.txt > gb3.txt

echo Testing...

ruby -v

echo
for i in kb.txt mb.txt gb1.txt gb2.txt gb3.txt
do
  echo
  echo "Running: time ruby readlines.rb $i"
  time ruby readlines.rb $i
  echo '---------------------------------------'
  echo "Running: time ruby foreach.rb $i"
  time ruby foreach.rb $i
  echo
done

rm [km]b.txt gb[123].txt 

它创建五个大小不断增加的文件。 1K 文件很容易处理,而且很常见。过去 1MB 文件被认为是大文件,但现在它们很常见。 1GB 在我的环境中很常见,并且会定期遇到超过 10GB 的文件,因此了解 1GB 及以上会发生什么情况非常重要。

将其保存为“readlines.rb”。它不做任何事情,只是在内部逐行读取整个文件,并将其附加到然后返回的数组中,而且看起来它会很快,因为它都是用 C 编写的:

lines = File.readlines(ARGV.shift).size
puts "#{ lines } lines read"

将其保存为“foreach.rb”:

lines = 0
File.foreach(ARGV.shift) { |l| lines += 1 }
puts "#{ lines } lines read"

Running sh ./test.sh在我的笔记本电脑上我得到:

Building test files...
Testing...
ruby 2.1.2p95 (2014-05-08 revision 45877) [x86_64-darwin13.0]

读取1K文件:

Running: time ruby readlines.rb kb.txt
28 lines read

real    0m0.998s
user    0m0.386s
sys 0m0.594s
---------------------------------------
Running: time ruby foreach.rb kb.txt
28 lines read

real    0m1.019s
user    0m0.395s
sys 0m0.616s

读取1MB文件:

Running: time ruby readlines.rb mb.txt
27028 lines read

real    0m1.021s
user    0m0.398s
sys 0m0.611s
---------------------------------------
Running: time ruby foreach.rb mb.txt
27028 lines read

real    0m0.990s
user    0m0.391s
sys 0m0.591s

读取1GB文件:

Running: time ruby readlines.rb gb1.txt
27027028 lines read

real    0m19.407s
user    0m17.134s
sys 0m2.262s
---------------------------------------
Running: time ruby foreach.rb gb1.txt
27027028 lines read

real    0m10.378s
user    0m9.472s
sys 0m0.898s

读取2GB文件:

Running: time ruby readlines.rb gb2.txt
54054055 lines read

real    0m58.904s
user    0m54.718s
sys 0m4.029s
---------------------------------------
Running: time ruby foreach.rb gb2.txt
54054055 lines read

real    0m19.992s
user    0m18.765s
sys 0m1.194s

读取3GB文件:

Running: time ruby readlines.rb gb3.txt
81081082 lines read

real    2m7.260s
user    1m57.410s
sys 0m7.007s
---------------------------------------
Running: time ruby foreach.rb gb3.txt
81081082 lines read

real    0m33.116s
user    0m30.790s
sys 0m2.134s

注意如何readlines每次文件大小增加时,运行速度都会慢两倍,并且使用foreach线性减慢。在 1MB 时,我们可以看到有一些东西会影响“slurping”I/O,但不会影响逐行读取。而且,由于 1MB 文件如今非常常见,因此很容易看出,如果我们不提前考虑,它们会在程序的生命周期内减慢文件的处理速度。这里只有几秒钟,或者当它们发生一次时没有太多,但如果它们每分钟发生多次,到年底就会对性能产生严重影响。

几年前,我在处理大型数据文件时遇到了这个问题。我使用的 Perl 代码会定期停止,因为它在加载文件时重新分配内存。重写代码以不读取数据文件,而是逐行读取和处理它,这极大地提高了运行速度,从运行五分钟多到不到一分钟,这给了我一个重要的教训。

“slurping”文件有时很有用,特别是当您必须跨行边界执行某些操作时,但是,如果必须这样做,则值得花一些时间考虑读取文件的替代方法。例如,考虑维护一个由最后“n”行构建的小缓冲区并扫描它。这将避免由于尝试读取和保存整个文件而导致的内存管理问题。这在 Perl 相关博客中进行了讨论“Perl Slurp-Eaze”其中涵盖了“何时”和“为什么”来证明使用完整文件读取的合理性,并且非常适用于 Ruby。

对于不要“吞食”文件的其他绝佳理由,请阅读“如何在文件文本中搜索模式并将其替换为给定值".

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么“吞食”文件不是一个好习惯? 的相关文章

  • Rails 中的代码片段应该放在哪里?

    我有这个代码片段 可以为 POST 生成签名 它的细节并不重要 但我想知道的是 由于它不是与模型相关的代码块 所以它确实可以在任何地方使用 在控制器中 在模型中 在视图助手中 即使在视图中 因此 我不确定在哪里 甚至更大的问题是 一旦将其放
  • 在 Ruby 中的文件中查找并替换

    我有一个用 ruby 编写的小程序 我在这里找到了一段很好的代码 用于查找和替换文件中的某些内容 但它似乎不起作用 这是代码 usr bin env ruby DOC test txt FIND M SEP n make substitut
  • 如何使用 ruby​​zip 库获取压缩文件的内容?

    我正在尝试提取上传的 zip 文件并将其内容存储在数据库中 每个文件一个条目 rubyzip 库几乎没有有用的文档 有一个资产表 其中包含键 string 文件名 和数据 binary 文件内容 我正在使用 ruby zip 库 并且已经做
  • 使用 ruby​​ Net::SSH 通过 sudo 读取远程文件

    我必须读取我有权 sudo 读取的远程文件的内容 猫 少或尾巴 我将在 Ruby 中执行此操作 因此我认为应该使用 Net SSH 来执行此操作 该文件是一个日志文件 因此可能会很大 这是我现在正在尝试的代码 require rubygem
  • Ruby,通过 SSH 和 LOG 逐一运行 linux 命令

    我想用 Ruby 女巫 net ssh 编写代码 在远程 Linux 机器上一一运行命令并记录所有内容 在 Linux 机器上称为命令 stdout 和 stderr 所以我写函数 def rs ssh cmds cmds each do
  • 如何将文件中的行读入数组?

    这就是我想做的 但有一句话 lines Array new File open test txt each line lines lt lt line 可能的 执行如下操作 File readlines test txt Read 文档 h
  • 字符串被两个不同的分隔符分割

    我有这样的字符串 some dasd dasd dasdas dasdas dasd das dsad 我需要用两个不同的符号将字符串拆分为数组 and 所以我想得到数组 some dasd dasd dasdas dasdas dasd
  • Ruby 中 SecureRandom.urlsafe_base64(8) 的碰撞概率?

    我在用SecureRandom urlsafe base64 8 为了在我的系统中创建 URL 安全的唯一 ID 我想知道如何计算碰撞概率 我将大约 10 000 个这些 id 插入到一个数组中 我想避免检查其中一个键是否已经在数组中 但我
  • Java - 了解 PrintWriter 和刷新的需要

    好吧 首先我对所有代码表示歉意 但我觉得代码太多总比代码不够好 我正在制作一个简单的聊天客户端和印刷机 尤其是我正在努力解决的问题 使用现在的代码 它将与服务器类交互 并且完美地打印我想要打印的内容 但是 当我删除 writer flush
  • Ruby 中实现的所有设计模式的备忘单?

    我想知道是否有针对 Ruby 中实现的所有设计模式的备忘单 这样您就不必重新发明轮子 设计模式对于组织大量代码非常有用 因为您不需要像在 verbose algol derivitive language 中那样编写那么多代码来在 ruby
  • 使用自定义 gem 在 Dreamhost/Passenger 上部署 Sinatra 应用程序

    我有一个 Sinatra 应用程序 正在尝试在 Dreamhost 上运行 该应用程序利用 pony 发送电子邮件 为了让应用程序从一开始就启动并运行 在添加小马之前 我必须gem unpack rack and gem unpack si
  • 安装了 Rails 但它说我没有:)

    我刚刚执行了这个命令来安装 Rails gem install rails 它似乎运行良好并安装了东西 然后当我按照本教程进行操作时 http guides rubyonrails org getting started html http
  • 如何在 Rails 3 中连接表并计算记录数?

    我有一个Collection有很多硬币的类 我正在尝试选择拥有两枚以上硬币的收藏品 目前 我可以直接通过 Ruby 来完成此操作 但效率极低 我当前的代码 collections Collection all select c c coin
  • sinatra 应用程序在运行时无法启动

    我使用的是 Ubuntu 10 10 Ruby 1 9 2 无论我做什么 我都无法在本地计算机上启动 sinatra 应用程序 你好 rb require sinatra get do Hello World end ruby hello
  • 不将所需的文件包含到 vim 全方位补全中

    如果我尝试在具有 require xxx 语句的 Ruby 文件中自动完成 它会开始扫描所需的所有文件 以及所需文件所需的文件 它每次都会这样做 是否可以使 vim 自动完成功能不扫描所需文件或仅扫描特定路径中的文件 例如仅 app 以下之
  • Rails:将参数从视图传递到控制器

    我在 Rails 中有以下模型 class Task lt ActiveRecord Base attr accessible description name project belongs to project validates na
  • golang中如何将相对路径解析为绝对路径?

    节点中是否有类似 path resolve 的API 或者有什么东西可以做同样的事情 例如 nodejs代码 path resolve sample sh 应该得到 home currentuser sample sh 解决 表示用户主目录
  • 在 RSpec 测试期间抑制控制台输出

    我正在测试在控制台上放置一些消息的类 包含 put p 警告等 我只是想知道在 RSpec 测试期间是否有能力抑制此输出 我压抑puts通过重定向在我的类中输出 stout到一个文本文件 这样 如果我出于任何原因需要查看输出 它就在那里 但
  • ActiveRecord 查询,按关联排序,最后一个 has_many

    我试图列出所有Users by the created at最近创建的关联记录 通讯 列 到目前为止我所拥有的 User includes communications order communications created at IS
  • Ruby:基于控制台的菜单

    我有一个名称和 URL 数组 并希望以向上 向下滚动菜单的形式向用户呈现名称列表 基本上是什么dialog允许在外壳内 我调查过ncurses ruby rdialog and HighLine但它们似乎要么作为一个项目被放弃 要么甚至从它

随机推荐

  • PHP MYSQL 如果存在则更新,如果不存在则插入?

    我不知道这是否正确 我有一个类 如果字段当前存在 我想更新数据库 如果不存在则插入 复杂的是我正在连接 3 个表 set colors school art baseimage 任何帮助都会非常好 这是我所拥有的 public functi
  • 是否可以使用 Node.js 制作键盘记录器? [关闭]

    Closed 这个问题需要细节或清晰度 目前不接受答案 我使用 Node 一年了 但仅用于编写服务器应用程序 我现在有兴趣制作一个通常用 C 或 C 等语言编写的应用程序 因此我不确定是否可以使用 JavaScript 和 Node js
  • xmlstarlet 更新值没有任何反应

    我有一个 xml 文件
  • 我的 Perlin Noise 函数有太多黑点和白色小条

    我通过大致遵循 C 中的 java 教程创建了 Perlin 噪声函数 然后我使用 Unity 对其进行可视化 我认为问题不在于 Unity 因为它们的内置功能运行良好 问题是有太多大黑点 这是一张照片 这就是我想要的样子 正如您所看到的
  • python 脚本未保存到数据库中

    我目前正在学习如何使用 Visual Studio 和 sqlite 使用 python 修改数据 我的任务是计算在文件中找到电子邮件的次数 然后以对每封电子邮件进行计数的方式组织它们 然后我必须将这些输入到 SQLite 中 作为一个名为
  • 使用 Javascript 对象作为对象键

    我正在尝试设计一种方法来使用简单的 Javascript 对象 一级深键值对 作为另一个对象的键 我知道仅仅使用没有字符串化的对象会导致 Object object 被用作密钥 请参阅以下内容 在 JavaScript 中使用对象作为属性键
  • 尝试使用 Selenium 和 Python 通过使用框架和 Javascript 的网页登录时出现 ERR_TOO_MANY_REDIRECTS 错误

    我正在尝试自动登录网页以下载每日 xml 我知道我需要有我认为的实际框架网址 http shop braintrust gr shop store customerauthenticateform asp 我检查表单和字段 然后执行以下操作
  • CakePHP未定义索引错误

    我正在尝试访问已登录用户的电子邮件地址 并且我可以在除一个控制器之外的所有控制器中成功执行此操作 这是我收到的错误 Notice 8 Undefined index User APP View Layouts default ctp lin
  • Python pip 在安装过程中不会构建依赖项

    Pip 似乎没有在我的 Ubuntu 服务器上从源代码构建依赖项 但它总是在我的 OS X 机器上这样做 例如 当我尝试安装包时qiime在 conda 或 virtualenv 我都尝试过 环境中 安装大量的东西需要几秒钟的时间 而这些东
  • 在 java.util.Scanner.throwFor(未知来源) 错误

    private static int posNum Scanner scan new Scanner System in int input 0 boolean error if scan hasNextInt input scan nex
  • Matlab 和 MySQL 未找到合适的驱动程序

    我尝试在 Matlab 中使用以下代码连接到 MySQL 数据库 但收到错误消息 没有找到合适的驱动程序 jdbc mysql mydatabasehost amazonaws comMyDatabase databaseName MyDa
  • 我正在尝试向 UImageView 添加阴影

    我正在尝试向 UIImage 视图添加阴影 我得到了一个阴影 但它被剪裁到图像视图的边缘 我不确定为什么 因为我正确地将 uiimageview clipsToBounds 设置为 否 下面是代码 void addShadow UIGrap
  • 这里没有解决方案适用于我的“未找到‘PDO’类”。

    我知道这里有类似标题的问题 但似乎没有一个对我有用 因此创建一个新问题的原因 所以问题是 我有一个在本地主机上完美运行的网站 我确信它有 PDO 支持 但在我的共享主机上上传后 我得到了 Symfony Component Debug Ex
  • 确定两个整数之间的字典距离

    假设我们有字典顺序的整数3 5 6 9 10 12 or 0011 0101 0110 1001 1010 1100每个都有两个位设置 我想要的是找到说之间的距离 它们之间有多少个字典排列 而不进行实际的排列 3 and 5使用尽可能少的操
  • 如何设置环境变量 R_user 以在 python 中使用 rpy2

    我无法在 python 中运行 rpy2 用这个代码 import rpy2 robjects as robjects 以下是完整的例外情况 运行时错误 R USER 未定义 文件 d py r r python py 第 1 行 位于 i
  • 无法在 swift 中重载 viewDidLoad() 中的函数

    无法重载函数viewDidLoad 迅速 它给出了错误definition conflict with previous value at func joinString strings String gt String override
  • Mac Office 2011 VBA 和 Dylib

    我正在 Mac OS 中开发 Word 2011 插件 目前 我需要在 VBA 宏中编写代码以从另一个应用程序检索字符串 通过套接字通信 因此 基本上在 Windows 中 我可以简单地创建一个 DLL 它可以帮助我与其他应用程序进行 So
  • Google 报告 API V4 问题

    我只是用 python 来了解 Google Analytics Reporting API 并一直在尝试让他们的演示文件工作并提取一些数据 完整的示例代码可以在这里找到 https developers google com analyt
  • Spring新事务与Retryable相结合

    如果我有一个方法 对于某个异常有 Spring 可重试 并且还有一个 Transactional Requires new 那么每次重试完成时 它会创建一个新事务还是使用现有事务 ie Retryable maxAttempts 5 bac
  • 为什么“吞食”文件不是一个好习惯?

    为什么 slurping 文件对于普通文本文件 I O 来说不是一个好的做法 它什么时候有用 例如 为什么我不应该使用这些 File read path to text txt lines each do line do something