在PHP中读取非常大的文件

26 php file large-files

fopen当我尝试读取一个非常适中的文件时失败了PHP. A 6 meg file让它窒息,虽然周围的小文件100k很好.我读过,有时需要重新编译PHP-D_FILE_OFFSET_BITS=64以读取在20场演出什么可笑的文件标记,但我不应该有一个6兆的文件没有问题?最终我们想要读取大约100兆的文件,并且能够打开它们然后逐行读取它们将很好,因为我可以使用较小的文件.

您在非常大的文件上阅读和操作的技巧/解决方案是什么PHP

更新:这是一个在我的6 meg文件上失败的简单代码块的示例 - PHP似乎没有抛出错误,它只返回false.也许我正在做一些非常愚蠢的事情?

$rawfile = "mediumfile.csv";

if($file = fopen($rawfile, "r")){  
  fclose($file);
} else {
  echo "fail!";
}
Run Code Online (Sandbox Code Playgroud)

另一个更新:感谢大家的帮助,它确实是一个令人难以置信的愚蠢 - 权限问题.当较大的文件没有时,我的小文件莫名其妙地具有读权限.卫生署!

Con*_*oyP 49

你确定它是fopen失败的而不是你的脚本的超时设置吗?默认值通常约为30秒左右,如果您的文件花费的时间比读取的时间长,则可能会将其绊倒.

另一件需要考虑的事情可能是脚本的内存限制 - 将文件读入数组可能会超过此值,因此请检查错误日志中的内存警告.

如果以上都不是您的问题,您可能会考虑使用fgets逐行读取文件,然后进行处理.

$handle = fopen("/tmp/uploadfile.txt", "r") or die("Couldn't get handle");
if ($handle) {
    while (!feof($handle)) {
        $buffer = fgets($handle, 4096);
        // Process buffer here..
    }
    fclose($handle);
}
Run Code Online (Sandbox Code Playgroud)

编辑

PHP似乎没有抛出错误,它只返回false.

$rawfile相对于脚本运行位置的路径是否正确?也许尝试在这里为文件名设置一个绝对路径.

  • 只有可能的解决方案如何打开真正的大文件.我正在处理这个解决方案1.5GB文件没有任何问题.所有其他解决方案,如文件的file_get_contents将读取整个文件到内存.这种方法是逐行处理的. (4认同)
  • 对我来说,stream_get_line比fgets更快,请查看此比较https://gist.github.com/joseluisq/6ee3876dc64561ffa14b (2认同)

Al-*_*unk 8

使用1.3GB文件和9.5GB文件进行了2次测试.

1.3 GB

运用 fopen()

此过程使用15555 ms进行计算.

它在系统调用中花费了169毫秒.

运用 file()

此过程使用6983 ms进行计算.

它在系统调用中花费了4469毫秒.

9.5 GB

运用 fopen()

此过程使用113559 ms进行计算.

它在系统调用中花费了2532毫秒.

运用 file()

此过程使用8221 ms进行计算.

它在系统调用中花费了7998毫秒.

似乎file()更快.


Tin*_*arb 5

fgets()直到文本文件超过20 MB 并且解析速度大大降低时,该功能才能正常运行。

• 该file_ get_contents()函数在 40 MB 之前给出良好的结果,在 100 MB 之前给出可接受的结果,但file_get_contents()将整个文件加载到内存中,因此它不可扩展。

• 该file()函数对于大文本文件是灾难性的,因为该函数创建一个包含每行文本的数组,因此该数组存储在内存中,使用的内存更大。
实际上,一个 200 MB 的文件我只能设法解析memory_limit为 2 GB,这对于我打算解析的 1+ GB 文件来说是不合适的。

当你要解析大于1GB的文件,解析时间超过15秒,又想避免将整个文件加载到内存中时,就得另辟蹊径了。

我的解决方案是解析任意小块中的数据。代码是:

$filesize = get_file_size($file);
$fp = @fopen($file, "r");
$chunk_size = (1<<24); // 16MB arbitrary
$position = 0;

// if handle $fp to file was created, go ahead
if ($fp) {
   while(!feof($fp)){
      // move pointer to $position in file
      fseek($fp, $position);

      // take a slice of $chunk_size bytes
      $chunk = fread($fp,$chunk_size);

      // searching the end of last full text line (or get remaining chunk)
      if ( !($last_lf_pos = strrpos($chunk, "\n")) ) $last_lf_pos = mb_strlen($chunk);

      // $buffer will contain full lines of text
      // starting from $position to $last_lf_pos
      $buffer = mb_substr($chunk,0,$last_lf_pos);
      
      ////////////////////////////////////////////////////
      //// ... DO SOMETHING WITH THIS BUFFER HERE ... ////
      ////////////////////////////////////////////////////

      // Move $position
      $position += $last_lf_pos;

      // if remaining is less than $chunk_size, make $chunk_size equal remaining
      if(($position+$chunk_size) > $filesize) $chunk_size = $filesize-$position;
      $buffer = NULL;
   }
   fclose($fp);
}
Run Code Online (Sandbox Code Playgroud)

使用的内存只有$chunk_size,速度比用 获得的略慢file_ get_contents()。我认为 PHP Group 应该使用我的方法来优化它的解析功能。

*)这里找到get_file_size()函数。