PHP几个快速读取大文件例子
在PHP中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file、file_get_contents之类的函数,简简单单的几行代码就能很漂亮的完成我们所需要的功能。但当所操作的文件是一个比较大的文件时,这些函数可能就显的力不从心, 下面将从一个需求入手来说明对于读取大文件时,常用的操作方法。
需求
有一个800M的日志文件,大约有500多万行, 用PHP返回最后几行的内容。
实现方法
1. 直接采用file函数来操作
由于 file函数是一次性将所有内容读入内存,而PHP为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,使服务器出现宕机,所以默认情况下限制只能最大使用内存16M,这是通过php.ini里的 memory_limit = 16M 来进行设置,这个值如果设置-1,则内存使用量不受限制。
下面是一段用file来取出这具文件最后一行的代码:
代码如下 | 复制代码 |
<?php ini_set('memory_limit', '-1'); $file = 'access.log'; $data = file($file); $line = $data[count($data) - 1]; echo $line; ?> |
整个代码执行完成耗时 116.9613 (s)。
我机器是2个G的内存,当按下F5运行时,系统直接变灰,差不多20分钟后才恢复过来,可见将这么大的文件全部直接读入内存,后果是多少严重,所以不在万 不得以,memory_limit这东西不能调得太高,否则只有打电话给机房,让reset机器了。
2.直接调用Linux的 tail 命令来显示最 后几行
在Linux命令行下,可以直接使用 tail -n 10 access.log 很轻易的显示日志文件最后几行,可以直接用PHP来调用tail命令,执行PHP代码如下:
代码如下 | 复制代码 |
<?php $file = 'access.log'; $file = escapeshellarg($file); // 对命令行参数进行安全转义 $line = `tail -n 1 $file`; echo $line; ?> |
整个代码执行完成耗时 0.0034 (s)
3. 直接使用PHP的 fseek 来进行文件操作
这种方式是最为普遍的方式,它不需要将文件的内容全部读入内容,而是直接通过指针来操作,所以效率是相当高效的。在使用fseek来对文件进行操作时,也有多种不同的方法,效率可能也是略有差别的,下面是常用的两种方法:
方法一
首先通过fseek找到文件的最后一位EOF,然后找最后一行的起始位置,取这一行的数据,再找次一行的起始位置, 再取这一行的位置,依次类推,直到找到了$num行。
#实现代码如下
代码如下 | 复制代码 |
<?php $fp = fopen($file, "r"); $line = 10; $pos = -2; $t = " "; $data = ""; while ($line > 0) { while ($t != "\n") { fseek($fp, $pos, SEEK_END); $t = fgetc($fp); $pos--; } $t = " "; $data .= fgets($fp); $line--; } fclose($fp); echo $data ?> |
整个代码执行完成耗时 0.0095 (s)
方法二
还是采用fseek的方式从文件最后开始读,但这时不是一位一位的读,而是一块一块的读,每读一块数据时,就将读取后的数据放在一个buf里,然后通过换 行符(\n)的个数来判断是否已经读完最后$num行数据。
#实现代码如下
代码如下 | 复制代码 |
<?php $fp = fopen($file, "r"); $num = 10; $chunk = 4096; $fs = sprintf("%u", filesize($file)); $max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file); for ($len = 0; $len < $max; $len += $chunk) { $seekSize = ($max - $len > $chunk) ? $chunk : $max - $len; fseek($fp, ($len + $seekSize) * -1, SEEK_END); $readData = fread($fp, $seekSize) . $readData; if (substr_count($readData, "\n") >= $num + 1) { preg_match("!(.*?\n){" . ($num) . "}$!", $readData, $match); $data = $match[0]; break; } } fclose($fp); echo $data; ?> |
整个代码执行完成耗时 0.0009(s)。
方法三
代码如下 | 复制代码 |
<?php return array_slice($lines, 0, $n); var_dump(tail(fopen("access.log", "r+"), 10)); |
整个代码执行完成耗时 0.0003(s)
php输出控制只影响到echo输出不会影响到位cookie,session,header这些函数了,下面来看下本文章。1、简介
当PHP脚本有输出时,输出控制函数可以用这些来控制输出。这在多种不同情况中非常有用,尤其是用来在脚本开始输出 数据后,发送http头信息到浏览器。输出控制函数不影响由 header()或setcookie()发送的文件头信息,仅影响像echo这样的函数和PHP代码块间的数据。
2、作用
1)、如官方介绍说,为了避免在输出文件头信息如header()和setcookie()之前出现输出而发生的错误。可以使用输出缓存函数。如下代码
代码如下 | 复制代码 |
<?php ob_start(); //打开缓存控制函数 echo 'hello'; header('Location: http://www.111cn.net/'); ?> |
2)、将输出内容放入缓存区,如需进行其他操作,如将输出写入缓存文件,可将缓冲区内容写入缓存文件,然后输出。
代码如下 | 复制代码 |
ob_start(); echo 'hello'; $file = ob_get_contents(); ob_end_clean(); write_cache($file); //将缓冲区内容写入缓存函数,实现细节省略 |
3)、获取某些无返回值内置函数的输出内容,如phpinnfo();
代码如下 | 复制代码 |
<?php ob_start(); phpinfo(); $file = ob_get_contents(); //phpinfo()输出内容 ob_end_clean(); ?> |
4)、在使用框架的时候,我们会发现框架中会将输出缓冲然后进行变量替换。最后输出。
除了这些内容,我们还可以利用输出缓存进行错误处理,具体看代码。我们可以将错误信息获取,以更加友好的方式输出。
代码如下 | 复制代码 |
<?php function display_error() { if($errors = error_get_last()) { return var_export($errors , TRUE); } return FALSE; } $dis_error = 'display_error'; //$a = 3; ob_start( $dis_error ); echo $a; |
附:ob_start()官方解释:
bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )
此函数将打开输出缓冲。当输出缓冲激活后,脚本将不会输出内容(除http标头外),相反需要输出的内容被存储在内部缓冲区中。
内部缓冲区的内容可以用 ob_get_contents() 函数复制到一个字符串变量中。 想要输出存储在内部缓冲区中的内容,可以使用 ob_end_flush() 函数。另外, 使用 ob_end_clean() 函数会静默丢弃掉缓冲区的内容。
output_callback
可选参数 output_callback 函数可以被指定。 此函数把一个字符串当作参数并返回一个字符串。 当输出缓冲区被( ob_flush(), ob_clean() 或者相似的函数)冲刷(送出)或者被清洗的时候;或者在请求结束之际输出缓冲区内容被冲刷到浏览器的时候该函数将会被调用。 当调用 output_callback 时,它将收到输出缓冲区的内容作为参数 并预期返回一个新的输出缓冲区作为结果,这个新返回的输出缓冲区内容将被送到浏览器。 如果这个 output_callback 不是一个可以调用的函数,此函数 会返回 FALSE 。
如果回调函数有两个参数,第二个参数会由一个位域补充,该位域由 PHP_OUTPUT_HANDLER_START, PHP_OUTPUT_HANDLER_CONT 和 PHP_OUTPUT_HANDLER_END 组成。
如果 output_callback 返回 FALSE ,其原来的输入 内容被直接送到浏览器。
这个参数 output_callback 可以通过直接给一个 NULL 值而避开。
ob_end_clean(), ob_end_flush(), ob_clean(), ob_flush() and ob_start() 不能从一个回调函数中调用。 如果从回调函数中调用了它们,产生的行为是不明确的。 如果想要删除缓冲区的内容,从回调函数中返回一个”" (空字符串)。 更不能从一个回调函数中使用像print_r($expression, true) 或highlight_file($filename, true) 一样的输出缓冲函数。
php,curl函数组可以帮助我们把机器为装成人行为来抓取网站,下面来分享两个例子,一个是访问http网页,一个访问https网页,下面一起看看。每次要使用curl的时候,总要查一堆资料。
现在将常用的几句保存下来,省的每次都去谷歌。
常规curl请求:
代码如下 | 复制代码 |
$url = 'http://www.111cn.net'; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HEADER, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($curl); curl_close($curl); var_dump($data); |
使用curl请求HTTPS:
代码如下 | 复制代码 |
$url = 'https://www.111cn.net'; $curl = curl_init(); curl_setopt($curl, CURLOPT_URL, $url); curl_setopt($curl, CURLOPT_HEADER, 1); curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);//这个是重点。 $data = curl_exec($curl); curl_close($curl); var_dump($data); |
注意
当请求https的数据时,会要求证书,这时候,加上下面这两个参数,规避ssl的证书检查
代码如下 | 复制代码 |
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE); // https请求 不验证证书和hosts curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE); |
在PHP中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file、file_get_contents之类的函数,简简单单的几行代码就能 很漂亮的完成我们所需要的功能。但当所操作的文件是一个比较大的文件时,这些函数可能就显的力不从心, 下面将从一个需求入手来说明对于读取大文件时,常用的操作方法。
需求需求
有一个800M的日志文件,大约有500多万行, 用PHP返回最后几行的内容。
实现方法
1. 直接采用file函数来操作
由于 file函数是一次性将所有内容读入内存,而PHP为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,使服务器出现宕机,所以默认情况下限制只能最大使用内存16M,这是通过php.ini里的 memory_limit = 16M 来进行设置,这个值如果设置-1,则内存使用量不受限制。
下面是一段用file来取出这具文件最后一行的代码:
代码如下 | 复制代码 |
<?php ini_set('memory_limit', '-1'); $file = 'access.log'; $data = file($file); $line = $data[count($data) - 1]; echo $line; ?> |
整个代码执行完成耗时 116.9613 (s)。
我机器是2个G的内存,当按下F5运行时,系统直接变灰,差不多20分钟后才恢复过来,可见将这么大的文件全部直接读入内存,后果是多少严重,所以不在万 不得以,memory_limit这东西不能调得太高,否则只有打电话给机房,让reset机器了。
2.直接调用Linux的 tail 命令来显示最 后几行
在Linux命令行下,可以直接使用 tail -n 10 access.log 很轻易的显示日志文件最后几行,可以直接用PHP来调用tail命令,执行PHP代码如下:
代码如下 | 复制代码 |
<?php $file = 'access.log'; $file = escapeshellarg($file); // 对命令行参数进行安全转义 $line = `tail -n 1 $file`; echo $line; ?> |
整个代码执行完成耗时 0.0034 (s)
3. 直接使用PHP的 fseek 来进行文件操作
这种方式是最为普遍的方式,它不需要将文件的内容全部读入内容,而是直接通过指针来操作,所以效率是相当高效的。在使用fseek来对文件进行操作时,也有多种不同的方法,效率可能也是略有差别的,下面是常用的两种方法:
方法一
首先通过fseek找到文件的最后一位EOF,然后找最后一行的起始位置,取这一行的数据,再找次一行的起始位置, 再取这一行的位置,依次类推,直到找到了$num行。
#实现代码如下
代码如下 | 复制代码 |
<?php $fp = fopen($file, "r"); $line = 10; $pos = -2; $t = " "; $data = ""; while ($line > 0) { while ($t != "\n") { fseek($fp, $pos, SEEK_END); $t = fgetc($fp); $pos--; } $t = " "; $data .= fgets($fp); $line--; } fclose($fp); echo $data ?> |
整个代码执行完成耗时 0.0095 (s)
方法二
还是采用fseek的方式从文件最后开始读,但这时不是一位一位的读,而是一块一块的读,每读一块数据时,就将读取后的数据放在一个buf里,然后通过换 行符(\n)的个数来判断是否已经读完最后$num行数据。
#实现代码如下
代码如下 | 复制代码 |
<?php $fp = fopen($file, "r"); $num = 10; $chunk = 4096; $fs = sprintf("%u", filesize($file)); $max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file); for ($len = 0; $len < $max; $len += $chunk) { $seekSize = ($max - $len > $chunk) ? $chunk : $max - $len; fseek($fp, ($len + $seekSize) * -1, SEEK_END); $readData = fread($fp, $seekSize) . $readData; if (substr_count($readData, "\n") >= $num + 1) { preg_match("!(.*?\n){" . ($num) . "}$!", $readData, $match); $data = $match[0]; break; } } fclose($fp); echo $data; ?> |
整个代码执行完成耗时 0.0009(s)。
方法三
代码如下 | 复制代码 |
<?php return array_slice($lines, 0, $n); var_dump(tail(fopen("access.log", "r+"), 10)); |
整个代码执行完成耗时 0.0003(s)
方法四,PHP的stream_get_line函数 ,读取快速,读取50万条数据大文件,大概需要20秒左右的时间!例子代码如下
代码如下 | 复制代码 |
$fp = fopen('./iis.log', 'r'); //文件 while (!feof($fp)) { //for($j=1;$j<=1000;$j++) { //读取下面的1000行并存储到数组中 $logarray[] = stream_get_line($fp, 65535, "\n"); // break; // } } |
今天整合了一下论坛discuz+问答系统tipask,按照官方后台操作配置了ucenter,实现的会员数据同步的功能
后来测试发现,只有从tipask(登录、注册)可以同步到discuz
从discuz登录确不能同步到tipask上面,于是在网上找了很多资料,也有很多网友出现了跟我一样的问题;都没有解决
实在没办法了,只能一点点的解读代码
发现uc_client下的client.php文件有这样一段话
====================================
代码如下 | 复制代码 |
function uc_user_synlogin($uid) { $uid = intval($uid); if(@include UC_ROOT.'./data/cache/apps.php') { if(count($_CACHE['apps']) > 1) { $return = uc_api_post('user', 'synlogin', array('uid'=>$uid)); } else { $return = ''; } } return $return; } |
====================================
上面意思是只有应用数大于一才会执行其他同步操作
于是去找了一下/data/cache/apps.php这个文件发现了问题;
默认配置好tipask+discuz的时候,这个文件并没有增加应用数,以前默认只有一个,下面那个应用是我手动加上了,
====================================
代码如下 | 复制代码 |
<?php $_CACHE['apps'] = array ( 1 => array ( 'appid' => '1', 'type' => 'DISCUZX', 'name' => 'Discuz! Board', 'url' => 'http://127.0.0.12', 'ip' => '', 'viewprourl' => '', 'apifilename' => 'uc.php', 'charset' => '', 'dbcharset' => '', 'synlogin' => '1', 'recvnote' => '1', 'extra' => false, 'tagtemplates' => '', 'allowips' => '', ), 2 => array ( 'appid' => '2', 'type' => 'OTHER', 'name' => 'ask', 'url' => 'http://127.0.0.12/ask', 'ip' => '', 'viewprourl' => '', 'apifilename' => 'uc.php', 'charset' => '', 'dbcharset' => '', 'synlogin' => '1', 'recvnote' => '1', 'extra' => false, 'tagtemplates' => '', 'allowips' => '', ), ); ?> |
=============================
上面的相关配置需要根据实际配置修改,这样问题就解决了;
相关文章
- 这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
- 安装curl扩展支持https是非常的重要现在许多的网站都使用了https了,下面我们来看一篇关于PHP安装curl扩展支持https例子吧。 问题: 线上运行的lamp服务器,默认yu...2016-11-25
- 在php中解析xml文档用专门的函数domdocument来处理,把json在php中也有相关的处理函数,我们要把数据xml 数据存到一个数据再用json_encode直接换成json数据就OK了。...2016-11-25
- 这篇文章主要介绍了c# 如何对CSV文件操作,帮助大家更好的理解和学习C#,感兴趣的朋友可以了解下...2020-11-03
- 本文主要介绍了python读取和保存mat文件的方法,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-08-25
- 首先如果要在程序中使用sdcard进行存储,我们必须要在AndroidManifset.xml文件进行下面的权限设置: 在AndroidManifest.xml中加入访问SDCard的权限如下: <!--...2016-09-20
- 这篇文章主要介绍了c#之FileStream对象读写大文件,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
- floor会产生小数了如果我们不希望有小数我们是可以去除小数点的了,下面一聚教程小编来为各位介绍php使用floor去掉小数点的例子,希望对各位有帮助。 float floor (...2016-11-25
- perl CPAN中有一个Tie-File 模块极大方便了对大文件的操作...2020-06-29
使用MSScriptControl 在 C# 中读取json数据的方法
下面小编就为大家带来一篇使用MSScriptControl 在 C# 中读取json数据的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25- 这篇文章主要介绍了C#实现appSettings节点读取与修改的方法,是非常实用的技巧,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了golang文件读取-按指定BUFF大小读取方式,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-12-22
- 本文主要介绍了Java读取PDF中的表格的方法示例,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-10-22
- 这篇文章主要介绍了R语言读取csv文件出错的解决方案,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-05-06
- 下在看一个利用fopen,file_get_contents读取本地服务器中.php文件的代码并显示的一些方法总结 如我有两个文件a.php,b.php。 a.php文件中的语句是: 代码如...2016-11-25
- 这篇文章主要介绍了Windows系统中使用C#读取文本文件内容的小示例,包括一次一行地读取文本文件的方法,需要的朋友可以参考下...2020-06-25
- 下面我们来看一篇关于纯Css实现下拉菜单的简单例子,希望这篇文章能够给各位同学带来帮助,具体步骤如下. 大家可能会经常用到hover这属性,用hover实现鼠标经过的颜...2017-01-22
- 这篇文章主要介绍了python 根据excel中颜色区分读取的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-06
- 这篇文章主要介绍了C#使用FileStream循环读取大文件数据的方法,结合实例形式分析了FileStream文件流的形式循环读取大文件的相关操作技巧,需要的朋友可以参考下...2020-06-25
- 这篇文章主要介绍了mybatis-plus读取JSON类型的方法实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-09-25