如何逐行读取大文件?

adn*_*ood 438 php

我想逐行读取文件,但没有完全加载到内存中.

我的文件太大而无法在内存中打开,如果尝试这样做,我总是会出现内存错误.

文件大小为1 GB.

cod*_*ict 651

您可以使用该fgets()函数逐行读取文件:

$handle = fopen("inputfile.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        // process the line read.
    }

    fclose($handle);
} else {
    // error opening the file.
} 
Run Code Online (Sandbox Code Playgroud)

  • 您没有在内存中读取整个文件.运行此操作所需的最大内存取决于输入中的最长行. (61认同)
  • @Brandin - Moot - 在这些情况下,提问题,即读取LINE BY LINE文件,没有明确定义的结果. (13认同)
  • 当然记得`fclose($ handle);`也是;) (10认同)
  • 这如何解释"在内存中打开太大"的部分? (3认同)
  • @ToolmakerSteve然后定义应该发生什么。如果需要,您可以仅打印消息“行太长;放弃”。这也是一个明确的结果。 (3认同)
  • 一行可以包含布尔假吗?如果是这样,则此方法将停止而不会到达文件末尾。此 URL http://php.net/manual/en/function.fgets.php 上的示例 #1 表明,即使尚未到达文件末尾,fgets 有时也会返回布尔值 false。在该页面的评论部分,人们报告说 fgets() 并不总是返回正确的值,因此使用 feof 作为循环条件更安全。 (2认同)

小智 125

if ($file = fopen("file.txt", "r")) {
    while(!feof($file)) {
        $line = fgets($file);
        # do same stuff with the $line
    }
    fclose($file);
}
Run Code Online (Sandbox Code Playgroud)

  • 我知道这是旧的,但是:不推荐使用while(!feof($ file)).[看看这里.](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) (8认同)
  • 正如@ Cuse70在他的回答中所说,如果文件不存在或无法打开,这将导致无限循环.在while循环之前测试`if($ file)` (7认同)
  • `feof()`不再存在了? (2认同)

els*_*hll 82

您可以为文件使用面向对象的接口类 - SplFileObject http://php.net/manual/en/splfileobject.fgets.php(PHP 5> = 5.1.0)

<?php

$file = new SplFileObject("file.txt");

// Loop until we reach the end of the file.
while (!$file->eof()) {
    // Echo one line from the file.
    echo $file->fgets();
}

// Unset the file to call __destruct(), closing the file handle.
$file = null;
Run Code Online (Sandbox Code Playgroud)

  • 谢谢.是的,例如你可以在$ file-> setFlags(SplFileObject :: DROP_NEW_LINE)之前添加这一行; 为了在一行末尾删除换行符. (5认同)
  • 更清洁的解决方案 谢谢;)还没有使用过这个类,这里​​有更多有趣的函数可以探索:http://php.net/manual/en/class.splfileobject.php (2认同)
  • 谢谢!另外,如果你不想要它们,使用`rtrim($ file-> fgets())`来删除每个读取的行字符串的尾随换行符. (2认同)
  • 更短:`foreach (new SplFileObject('file.txt') as $line) echo $line` (2认同)

Nin*_*pac 53

如果您要打开一个大文件,您可能希望使用生成器和fgets()来避免将整个文件加载到内存中:

/**
 * @return Generator
 */
$fileData = function() {
    $file = fopen(__DIR__ . '/file.txt', 'r');

    if (!$file)
        die('file does not exist or cannot be opened');

    while (($line = fgets($file)) !== false) {
        yield $line;
    }

    fclose($file);
};
Run Code Online (Sandbox Code Playgroud)

像这样使用它:

foreach ($fileData() as $line) {
    // $line contains current line
}
Run Code Online (Sandbox Code Playgroud)

这样您就可以在foreach()中处理单个文件行.

注意:生成器要求> = PHP 5.5

  • 这应该是一个可接受的答案.发电机的速度要快一百倍. (3认同)
  • 而且更节省内存。 (2认同)
  • @NinoŠkopac:你能解释一下为什么这个解决方案内存效率更高吗?例如,与“SplFileObject”方法相比。 (2认同)

Sta*_*arx 28

使用缓冲技术来读取文件.

$filename = "test.txt";
$source_file = fopen( $filename, "r" ) or die("Couldn't open $filename");
while (!feof($source_file)) {
    $buffer = fread($source_file, 4096);  // use a buffer of 4KB
    $buffer = str_replace($old,$new,$buffer);
    ///
}
Run Code Online (Sandbox Code Playgroud)

  • 这值得更多的爱,因为它可以处理巨大的文件,甚至是没有回车或超长行的文件...... (2认同)

NoI*_*Guy 28

有一个file()函数返回文件中包含的行数组.

foreach(file('myfile.txt') as $line) {
   echo $line. "\n";
}
Run Code Online (Sandbox Code Playgroud)

  • 一个GB文件将全部读入内存并转换为多个GB阵列...祝你好运. (27认同)
  • 这不是问题的答案,但它确实回答了许多人在这里看到的更常见的问题,所以它仍然有用,谢谢. (4认同)
  • file()非常便于使用小文件.特别是当你想要一个array()作为最终结果时. (2认同)

Quo*_*ons 17

foreach (new SplFileObject(__FILE__) as $line) {
    echo $line;
}
Run Code Online (Sandbox Code Playgroud)


xan*_*dev 7

SplFileObject 在处理大文件时很有用。

function parse_file($filename)
{
    try {
        $file = new SplFileObject($filename);
    } catch (LogicException $exception) {
        die('SplFileObject : '.$exception->getMessage());
    }
    while ($file->valid()) {
        $line = $file->fgets();
        //do something with $line
    }

    //don't forget to free the file handle.
    $file = null;
}
Run Code Online (Sandbox Code Playgroud)


Joh*_*ohn 7

所有答复都没有明显的答案.PHP有一个整洁的流分隔符解析器,可用于此目的.

$fp=fopen("/path/to/the/file", "r+");
while ($line = stream_get_line($fp, 1024 * 1024, "\n"))
{
echo $line;
}
fclose($fp);
Run Code Online (Sandbox Code Playgroud)


Cus*_*e70 6

小心'while(!feof ... fgets()'的东西,fgets可以得到一个错误(returnfing false)并永远循环而不会到达文件的末尾.codaddict最接近正确但是当你的'while fgets'时循环结束,检查feof;如果不是,则出现错误.


Teg*_*der 5

这个问题的流行解决方案之一将涉及新线字符的问题.简单就可以很容易地修复它str_replace.

$handle = fopen("some_file.txt", "r");
if ($handle) {
    while (($line = fgets($handle)) !== false) {
        $line = str_replace("\n", "", $line);
    }
    fclose($handle);
}
Run Code Online (Sandbox Code Playgroud)


小智 5

这就是我如何处理非常大的文件(测试高达100G)的方式。它比fgets()更快

$block =1024*1024;//1MB or counld be any higher than HDD block_size*2
if ($fh = fopen("file.txt", "r")) { 
    $left='';
    while (!feof($fh)) {// read the file
       $temp = fread($fh, $block);  
       $fgetslines = explode("\n",$temp);
       $fgetslines[0]=$left.$fgetslines[0];
       if(!feof($fh) )$left = array_pop($lines);           
       foreach ($fgetslines as $k => $line) {
           //do smth with $line
        }
     }
}
fclose($fh);
Run Code Online (Sandbox Code Playgroud)

  • 如何确保 1024*1024 块不会在行中间中断? (2认同)
  • @user151496 简单!!数... 1.2.3.4 (2认同)