当前位置:首页 > CN2资讯 > 正文内容

如何使用PHP读取大文件(一)php 读取文件

1天前CN2资讯

作为PHP开发者,我们并需要经常担心内存管理。PHP引擎在后台为我们做了很好的清理工作,执行完上下文就短期释放的Web服务器模型意味着,就算是烂代码也不会产生太久的影响。


没错,有时候我们需要走出这个舒适区,比如当我们想在一个小的VPS上一个大项目中运行Composer,或者我们需要在同样小的服务器上读取大文件时。


测试成功的标准


确保我们的代码优化的唯一方法是测量出现不好的情况。接下来需要我们的应用修复后与另一个测量标准进行比较。换句话说,除非我们知道“解决方案”对我们有多大的帮助,


有两个度量值需要我们关注。一个是CPU使用率,它表示我们程序处理的过程有多快或者有多慢?第二是内存使用率。PHP脚本执行需要多少内存?这些通常是成反比的,这意味着我们要么将负载加到CPU上来卸掉内存的占用,反之亦然。


在一个异步执行模型(比如多进程或多线程PHP应用程序)中,CPU和内存使用是重要之考虑因素。在传统的PHP技术栈中,其中有一个达到服务器的限制时,这些都会成为较重的问题。


实时测量PHP执行时的CPU使用率是不切合实际的。如果你要关注此领域,在CentOS,Ubuntu或Mac上可以使用类似top式的命令,如果使用了Windows,可以考虑切换到Linux,以便使用top。


本篇文章的写作目的,就是要测量内存占用情况。我们将看看在传统PHP脚本中使用了多少内存,然后将执行一些优化策略来进行度量。然后,我希望你能做出一个有营养的选择。


以下是我们用来查看内存占用的函数:


// formatBytes is taken from the php.net documentation


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];

}


后面,我们会在正常执行的PHP脚本后使用这些函数,通过它可以看出哪个脚本占用内存最多。


我们要什么


在PHP中有很多方法能够读取文件。会有两种用法情况,可能想同时读取和处理文件数据,根据读取的内容处理输出数据或执行其它操作,也有可能只转换一下数据流,而不需要真正的访问数据。


试想一下,对于第一种情况,我们希望在读取文件时,每1000行做一个独立进程Job。那么在内存中至少要保留10000行,然后把它们传给队列的管理器。


对于第二种情况,如果我们是处理从API返回的特别大的数据,此时需要确保它以压缩方式存储。


这两种情况,都是在存取大尺寸文件。


首先,我们需要了解数据是什么,第二,我们不在乎数据是什么。让我们来探索这些选项。


逐行读取文件


有不少处理文件的函数,我们结合一些原生的文件读取函数来处理。如下代码:



// 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.5M,最高内存占用量为12.8M。现在让我们用一个生成器来逐行阅读。


// 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";


文本文件大小相同,但内存占用最高的峰值却只有393KB。在读取数据之前,我看到了两个空白行,也许可以把文档分成几段。


// 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";


猜猜我们现在用多少内存?即便我们把文本文件分成1216个块,我们仍然只使用459KB内存,这让你感到惊讶?鉴于generator的性质,我们需要迭代存储最大文本块的内存是101985个字符。


我已经写过使用生成器的性能提升,以及Nikita Popov's的Iterator库的性能提升,如果你喜欢可以再搜索这些内容。


生成器还有其它用途,但是这对于读取大文件的性能是非常明显的,如果我们读取这样的数据,生成器是最好的方法。


管道操作


还有一种情况,我们并不需要操作数据,只是想把一个文件的数据传递到另一个文件,这通常被称为管道(Pipe。大概是由于我们没有看到管道内部执行到结束,因为它是不透明的)。我们可以使用Stream的方法来实现。


首先编写一个PHP脚本来从一个文件导入到另一个文件,以便我们可以测试内存使用情况:


// from piping-files-1.php

file_put_contents(

    "piping-files-1.txt", file_get_contents("shakespeare.txt")

);


require "memory.php";


毫无意外,这个脚本要比它要复制的文件尺寸稍大的内存来运行,因为它必须读入文件并保存到内存,直至写入新文件。对于小文件,这个是没有问题的。但是一旦处理的是一个大文件,就没有那么大的内存了.....


让我们尝试从一个文件流到另一个文件(或管道):


// from piping-files-2.php

$handle1 = fopen("shakespeare.txt", "r");

$handle2 = fopen("piping-files-2.txt", "w");


stream_copy_to_stream($handle1, $handle2);


fclose($handle1);

fclose($handle2);


require "memory.php";



    你可能想看:

    扫描二维码推送至手机访问。

    版权声明:本文由皇冠云发布,如需转载请注明出处。

    本文链接:https://www.idchg.com/info/20219.html

    分享给朋友:

    “如何使用PHP读取大文件(一)php 读取文件” 的相关文章

    宝塔安装扩展不生效的解决方案与技巧

    在服务器管理的世界中,宝塔面板无疑是一款强大而受欢迎的工具。它让用户以更简单的方式进行服务器管理。通过宝塔,用户可以轻松管理网站、数据库和各种扩展。而PHPExcel等PHP扩展的安装显得尤为重要,因为它们提供了许多强大的功能,助力网站正常运行。 PHP扩展的重要性不言而喻。没有合适的扩展,网站可能...

    深入了解ICMP协议及其在网络管理中的应用

    ICMP(Internet Control Message Protocol,互联网控制消息协议)是TCP/IP协议族中的一种重要网络协议。我们可以把ICMP想象成网络中的信使,它主要负责在网络中传递控制消息和错误报告。这种功能对于维护网络的正常运作至关重要,让网络管理员能够及时发现并处理问题。IC...

    如何选择合适的IP站及其运作原理

    IP站的运作原理 在探讨IP站的运作原理之前,首先需要明确什么是IP站。简单来说,IP站是一种特殊的网络服务,它利用互联网协议(IP)提供不同的网络功能和服务。每个IP站都与一个或多个IP地址相连,能够用来访问信息、数据或应用程序。在我的网络体验中,无论是个人使用还是企业应用,IP站总是扮演着至关重...

    IP检测服务:简化网络体验与保护用户隐私

    IP检测服务是当今网络环境中不可或缺的一部分。简单来说,它帮助用户或开发者迅速获取他们的设备公网IP地址,同时提供各种网络信息。这项服务以其高效、便捷和免费的特点,吸引了众多用户和企业进行使用。 想获取公网IP地址往往需要复杂的步骤,而IP检测服务的出现使这个过程变得轻松。它支持多种返回格式,包括纯...

    黑五狂欢购物攻略:如何高效享受黑色星期五折扣

    黑五,全称“黑色星期五”(Black Friday),是每年感恩节后的第一天。随着这个节日的到来,许多商店和电商都会推出琳琅满目的促销活动,标志着圣诞购物季的开端。我第一次听到黑五,是在朋友们热火朝天地讨论即将到来的折扣和特卖,这种热情真是难以抗拒。 黑五起源于美国,为什么叫“黑色”呢?这个名字其实...

    Sharknode: 开启去中心化区块链的新篇章

    Sharknode生态系统是一个新兴的区块链平台,它为开发者和用户提供了一个强大的环境,以实现去中心化应用与服务。作为一个集成度极高的系统,Sharknode不仅支持了智能合约的创建与执行,还提供了高速、安全的交易体验。这个生态系统旨在突破传统区块链的局限,向用户开放更多可能性,为不同行业带来变革。...