站长资讯网
最全最丰富的资讯网站

聊聊怎么利用PHP读取大文件(教程分享)

PHP如何读取大文件?下面本篇文章给大家介绍一下利用PHP读取大文件的方法,希望对大家有所帮助!

聊聊怎么利用PHP读取大文件(教程分享)

推荐课程:《Vue + TP6 + Mysql 社交电商系统开发视频课》
API 文档、设计、调试、自动化测试一体化协作工具:点击使用

作为PHP开发人员,我们不需要担心内存管理。 PHP引擎在我们背后进行了出色的清理工作,短暂执行上下文的 web server 模型意味着即使是最草率的代码也没有持久的影响。

在极少数情况下,我们可能需要走出舒适的界限 — 例如,当我们尝试在可以创建的最小 VPS 上为大型项目运行 Composer 时,或者需要在同样小的服务器上读取大文件时。

这是我们将在本教程中讨论的一个问题。

本教程的代码可以在这里找到 GitHub。

衡量成功

唯一能确认我们对代码所做改进是否有效的方式是:衡量一个糟糕的情况,然后对比我们已经应用改进后的衡量情况。换言之,除非我们知道“解决方案”能帮我们到什么程度(如果有的话),否则我们并不知道它是否是一个解决方案。

我们可以关注两个指标。首先是CPU使用率。我们要处理的过程运行得有多快或多慢?其次是内存使用率。脚本执行要占用多少内存?这些通常是成反比的—这意味着我们能够以CPU使用率为代价减少内存的使用率,反之亦可。

在一个异步处理模型(例如多进程或多线程PHP应用程序)中,CPU和内存使用率都是重要的考量。在传统PHP架构中,任一达到服务器所限时这些通常都会成为一个麻烦。

测量PHP内部的CPU使用率是难以实现的。如果你确实关注这一块,可用考虑在Ubuntu或macOS中使用类似于 top 的命令。对于Windows,则可用考虑使用Linux子系统,这样你就能够在Ubuntu中使用 top 命令了。

在本教程中,我们将测量内存使用情况。我们将看一下“传统”脚本会使用多少内存。我们也会实现一些优化策略并对它们进行度量。最后,我希望你能做一个合理的选择。

以下是我们用于查看内存使用量的方法:

// formatBytes 方法取材于 php.net 文档  memory_get_peak_usage();  function formatBytes($bytes, $precision = 2) {     $units = array("b", "kb", "mb", "gb", "tb");      $bytes = max($bytes, 0);     $pow = floor(($bytes ? log($bytes) : 0) / log(1024));     $pow = min($pow, count($units) - 1);      $bytes /= (1 << (10 * $pow));      return round($bytes, $precision) . " " . $units[$pow]; }

我们将在脚本的结尾处使用这些方法,以便于我们了解哪个脚本一次使用了最多的内存。

我们有什么选择?

我们有许多方法来有效地读取文件。有以下两种场景会使用到他们。我们可能希望同时读取和处理所有数据,对处理后的数据进行输出或者执行其他操作。 我们还可能希望对数据流进行转换而不需要访问到这些数据。

想象以下,对于第一种情况,如果我们希望读取文件并且把每 10,000 行的数据交给单独的队列进行处理。我们则需要至少把 10,000 行的数据加载到内存中,然后把它们交给队列管理器(无论使用哪种)。

对于第二种情况,假设我们想要压缩一个 API 响应的内容,这个 API 响应特别大。虽然这里我们不关心它的内容是什么,但是我们需要确保它被以一种压缩格式备份起来。

这两种情况,我们都需要读取大文件。不同的是,第一种情况我们需要知道数据是什么,而第二种情况我们不关心数据是什么。接下来,让我们来深入讨论一下这两种做法…

逐行读取文件

PHP 处理文件的函数很多,让我们将其中一些函数结合起来实现一个简单的文件阅读器

// from memory.php  function formatBytes($bytes, $precision = 2) {     $units = array("b", "kb", "mb", "gb", "tb");      $bytes = max($bytes, 0);     $pow = floor(($bytes ? log($bytes) : 0) / log(1024));     $pow = min($pow, count($units) - 1);      $bytes /= (1 << (10 * $pow));      return round($bytes, $precision) . " " . $units[$pow]; }  print formatBytes(memory_get_peak_usage());
// from reading-files-line-by-line-1.php function readTheFile($path) {     $lines = [];     $handle = fopen($path, "r");      while(!feof($handle)) {         $lines[] = trim(fgets($handle));     }      fclose($handle);     return $lines; }  readTheFile("shakespeare.txt");  require "memory.php";

我们正在阅读一个包括莎士比亚全部著作的文本文件。该文件大小大约为 5.5 MB。内存使用峰值为 12.8 MB。现在,让我们使用生成器来读取每一行:

// from reading-files-line-by-line-2.php  function readTheFile($path) {     $handle = fopen($path, "r");      while(!feof($handle)) {         yield trim(fgets($handle));     }      fclose($handle); }  readTheFile("shakespeare.txt");  require "memory.php";

文件大小相同,但是内存使用峰值为 393 KB。这个数据意义大不大,因为我们需要加入对文件数据的处理。例如,当出现两个空白行时,将文档拆分为多个块:

// from reading-files-line-by-line-3.php  $iterator = readTheFile("shakespeare.txt");  $buffer = "";  foreach ($iterator as $iteration) {     preg_match("/n{3}/", $buffer, $matches);      if (count($matches)) {         print ".";         $buffer = "";     } else {         $buffer .= $iteration . PHP_EOL;     } }  require "memory.php";

有人猜测这次使用多少内存吗?即使我们将文本文档分为 126 个块,我们仍然只使用 459 KB 的内存。鉴于生成器的性质,我们将使用的最大内存是在迭代中需要存储最大文本块的内存。在这种情况下,最大的块是 101985 个字符。

我已经写过 使用生成器提高性能 以及 生成器扩展包,感兴趣的可以去查看

赞(0)
分享到: 更多 (0)