php操作大文件

看了http://hi.baidu.com/qiaoyuetian/item/76c51f0ce25030e4f45ba69e(php读取大文件详解),然后测试了里边的代码,发现一些错误,

总结,红色部分标注了修改,觉得还是用fseek中的第二种方法常用。

一个把操作文件函数解释的非常详细的博客:https://www.ibm.com/developerworks/cn/opensource/os-php-readfiles/ (好好看看)

需求如下:  现有一个1G左右的日志文件,大约有500多万行, 用php返回最后几行的内容。

1. 直接采用file函数来操作

注:由于 file函数是一次性将所有内容读入内存,而php为了防止一些写的比较糟糕的程序占用太多的内存而导致系统内存不足,使服务器出现宕机,所以默认情况下 限制只能最大使用内存16M,这是通过php.ini里的memory_limit = 16M来进行设置,这个值如果设置-1,则内存使用量不受限制.

//整个代码执行完成耗时 116.9613 (s).$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 &gt; $chunk) ? $chunk : $max - $len;
    fseek($fp, ($len + $seekSize) * -1, SEEK_END);
    $readData = fread($fp, $seekSize) . $readData;

    if (substr_count($readData, "\n") &gt;= $num + 1) {
        preg_match("!(.*?\n){".($num)."}$!", $readData, $match);
        $data = $match[0];
        break;
    }
}
fclose($fp);
echo $data;

我机器是2个G的内存,当按下F5运行时,系统直接变灰,差不多20分钟后才恢复过来,可见将这么大的文件全部直接读入内存,后果是多少严重,所以不在万不得以,memory_limit这东西不能调得太高,否则只有打电话给机房,让reset机器了。

2.直接调用linux的tail命令来显示最后几行:

//整个代码执行完成耗时 0.0034 (s)
file = ‘access.log‘;
$file = escapeshellarg($file); // 对命令行参数进行安全转义
$line = `tail -n 1 $file`;
echo $line;

3. 直接使用php的fseek来进行文件操作:

这种方式是最为普遍的方式,它不需要将文件的内容全部读入内存,而是直接通过指针来操作,所以效率是相当高效的.在使用fseek来对文件进行操作时,也有多种不同的方法,效率可能也是略有差别的,下面是常用的两种方法.
方法一:
首先通过fseek找到文件的最后一位EOF,然后找最后一行的起始位置,取这一行的数据,再找次一行的起始位置,再取这一行的位置,依次类推,直到找到了$num行。

**这个方法测试时有问题,看注释。

//整个代码执行完成耗时 0.0095 (s)
function tail($fp,$n,$base=5)
{
    assert($n>0);
    $pos = $n+1;
    $lines = array();
    while(count($lines)< =$n){  //$n为想获取的行数,每次外围循环一个向$lines插入一条数据
        try{
            fseek($fp,-$pos,SEEK_END);  //把文件指针指向从后向前的-$pos位置,例如 fseek($fp,-2, SEEL_END)就是把指针指向文件的最后一个字符,不是倒数第二个是因为倒数第一个是\n。
        } catch (Exception $e){
            fseek(0);
            break;
        }
        $pos *= $base;   //问题出在这里,当下一次循环进来时$pos有可能还是指在最后一行的位置上,不能保证每次都指到了上一行。
        while(!feof($fp)){
            //这里也有个问题,            //这个循环会循环两次,第二次插入数组的值为\n,比如当fseek指在最后一行时,fgets读取了最后一行的数据,但不包含\n,第二次读取了\n,所以数组插入了两条。        array_unshift($lines,fgets($fp));        //break; 
        }
    }
    return array_slice($lines,0,$n);
}
var_dump(tail(fopen("access.log","r+"),10));

方法二:
还是采用fseek的方式从文件最后开始读,但这时不是一位一位的读,而是一块一块的读,每读一块数据时,就将读取后的数据放在一个buf里,然后通过换行符(\n)的个数来判断是否已经读完最后$num行数据.

//整个代码执行完成耗时 0.0009(s).
$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

4.修改php.ini的内存:

//整个代码执行完成耗时 0.0003(s)
ini_set(‘memory_limit‘,‘-1‘);  //不限制
$file = ‘access.log‘;
$data = file($file);
$line = $data[count($data)-1];
echo $line; 
时间: 2024-08-01 12:40:30

php操作大文件的相关文章

jdbc mysql crud dao模型 sql注入漏洞 jdbc 操作大文件

day17总结 今日内容 l JDBC 1.1 上次课内容总结 SQL语句: 1.外键约束:foreign key * 维护多个表关系! * 用来保证数据完整性! 2.三种关系: * 一对多: * 一个客户可以对应多个订单,一个订单只属于一个客户! * 建表原则: * 在多的一方创建一个字段,作为外键指向一的一方的主键!!! * 多对多: * 一个学生可以选择多个课程,一个课程也可以被多个学生选择! * 建表原则: * 创建第三张表,第三张表中放入两个字段,作为外键分别指向多对多双方的主键! *

java 读写操作大文件 BufferedReader和RandomAccessFile

一 老问这问题,两个都答出来算加分项? 二 具体代码如下,没什么好说的直接说对比. BufferedReader和RandomAccessFile的区别RandomAccessFile 在数据越大,性能越差.因为他是数据文件的一个channel,支持读改原数据文件. BufferedReader是读改数据文件的一个在内存的副本. 那RandomAccessFile的优点? 1.RandomAccessFile忽略了字符编码的处理,加快了处理速度 2.若是对数据操作在BufferedReader创

java操作大文件复制

1.大文件的复制可以用Java nio中的channel-to-channel传输,Channel-to-channel传输是可以极其快速的,特别是在底层操作系统提供本地支持的时候.某些操作系统可以不必通过用户空间传递数据而进行直接的数据传输.对于大量的数据传输,这会是一个巨大的帮助. 2.代码 package com.dingwang.File; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; i

文件操作--大文件的加密和搬运

C文件操作综合项目: 读取一个文件的数据到另一个文件,在读取的过程中加密. #include "stdlib.h" #include "stdio.h" #include "string.h" int fileEnc(char* fileDestinationPath, char* fileSourcePath) { FILE *fileDestination = NULL; FILE *fileSource = NULL; int ret =

Python 文件操作----大文件处理

#coding=utf-8 #当读取某个不知名的文件时,切记不要用read(),否则一定会报异常 #1.获取用户名要复制的文件 old_file_name = input("输入要复制的文件名:") #2.打开要复制的文件 old_file = open(old_file_name,"r") position = old_file_name.rfind(".") new_file_name = old_file_name[:position] +

PHP读取CSV大文件导入数据库的示例

对于数百万条数据量的CSV文件,文件大小可能达到数百M,如果简单读取的话很可能出现超时或者卡死的现象. 为了成功将CSV文件里的数据导入数据库,分批处理是非常必要的. 下面这个函数是读取CSV文件中指定的某几行数据: /** * csv_get_lines 读取CSV文件中的某几行数据 * @param $csvfile csv文件路径 * @param $lines 读取行数 * @param $offset 起始行数 * @return array * */ function csv_get

PHP快速按行读取CSV大文件的封装类分享(也适用于其它超大文本文件)

CSV大文件的读取已经在前面讲述过了(PHP按行读取.处理较大CSV文件的代码实例),但是如何快速完整的操作大文件仍然还存在一些问题. 1.如何快速获取CSV大文件的总行数? 办法一:直接获取文件内容,使用换行符进行拆分得出总行数,这种办法对小文件可行,处理大文件时不可行:办法二:使用fgets一行一行遍历,得出总行数,这种办法比办法一好一些,但大文件仍有超时的可能:办法三:借助SplFileObject类,直接将指针定位到文件末尾,通过SplFileObject::key方法获取总行数,这种办

java与python在处理大文件操作上的对比

1.问题描述 现在对一个2g的大文件,抽取第二列含有特点16个串的信息,并将这些含有特串的信息,写回到两个文件中 2.具体实现 (1)java代码 package naifen; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java

java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射

java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer. MappedByteBuffer是java nio引入的文件内存映射方案,读写性能极高.NIO最主要的就是实现了对异步操作的支持.其中一种通过把一个套接字通道(SocketChannel)注册到一个选择器(Selector)中,不时调用后者的选择(select)方法就能返回满足的选择键(SelectionKey