如何逐行读取大文件?


Answers:


684

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

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

    fclose($handle);
} else {
    // error opening the file.
} 

3
这如何说明too large to open in memory零件?
Starx 2012年

64
您没有读取内存中的整个文件。运行此命令所需的最大内存取决于输入中的最长行。
codaddict

13
@Brandin-模拟-在这种情况下,被问到要逐行读取文件的问题没有明确的结果。
ToolmakerSteve

3
@ToolmakerSteve然后定义应该发生什么。如果需要,您可以仅打印消息“行太长;放弃”。这也是一个明确的结果。
Brandin

2
一行可以包含布尔值false吗?如果是这样,则此方法将停止而不到达文件末尾。此URL上的示例#1 php.net/manual/en/function.fgets.php建议,即使尚未到达文件结尾,fgets有时也可能返回布尔值false。在该页面的评论部分,人们报告fgets()并不总是返回正确的值,因此将feof用作循环条件是更安全的。
cjohansson

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

8
正如@ Cuse70在他的回答中所说,如果文件不存在或无法打开,这将导致无限循环。测试if($file)while循环之前
FrancescoMM

10
我知道这很旧,但是:不建议使用while(!feof($ file))。在这里看看。
Kevin Van Ryckegem 2015年

顺便说一句:“如果在文件指针中没有更多的数据可读取,则返回FALSE。” php.net/manual/en/function.fgets.php ...以防万一
每个人

2
feof()不再存在了?
瑞安·杜瓦

94

您可以将面向对象的接口类用于文件-SplFileObject http://php.net/manual/zh-CN/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;

3
更清洁的解决方案。谢谢;)还没有使用过此类,这里有更多有趣的功能可以探索:php.net/manual/en/class.splfileobject.php
Lukas Liesis 2015年

6
谢谢。是的,例如,您可以在$ file-> setFlags(SplFileObject :: DROP_NEW_LINE);之前添加此行。为了将换行符放在行尾。
elshnkhll,2015年

据我eof()所知,SplFileObject中没有函数?
Chud37

3
谢谢!另外,rtrim($file->fgets())如果您不希望使用它们,则可以为每个读取的行字符串剥离尾随的换行符。
racl101

@ Chud37是的,那里是:php.net/manual/en/splfileobject.eof.php
Nathan F.

59

如果要打开一个大文件,则可能希望将Generators与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);
};

像这样使用它:

foreach ($fileData() as $line) {
    // $line contains current line
}

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

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


3
相反,这应该是一个公认的答案。使用发电机的速度快一百倍。
塔奇

1
并且更加节省内存。
NinoŠkopac'18

2
@NinoŠkopac:您能解释一下为什么这种解决方案具有更高的内存效率吗?例如,与该SplFileObject方法相比。
k00ni

30

使用缓冲技术读取文件。

$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);
    ///
}

2
这值得更多的爱,因为它将适用于大文件,甚至没有回车符或行太长的文件……
Jimmery 2015年

如果OP不在乎真正的行,而只是想提供下载服务,我不会感到惊讶。在这种情况下,这个答案就很好了(无论如何大多数PHP程序员都会这么做)。
阿尔瓦罗·冈萨雷斯

30

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

foreach(file('myfile.txt') as $line) {
   echo $line. "\n";
}

28
一个GB的文件将全部读入内存,并转换为一个以上的GB阵列...祝您好运。
FrancescoMM

4
这不是所问问题的答案,但确实回答了许多人在这里看到的更常见的问题,因此它仍然很有用,谢谢。
pilavdzice '16

2
file()对于处理小文件非常方便。尤其是当您希望将array()作为最终结果时。
functionvoid

对于更大的文件,这是个坏主意,因为整个文件会一次被读取到一个数组中
Flash Thunder

这在大文件上会严重中断,因此恰恰是该方法不起作用。
ftrotter


17

并非所有的答复中都有明显的答案。
PHP具有一个精巧的流分隔符解析器,可用于此目的。

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

应该注意的是,该代码将仅返回行,直到出现第一个空行。您需要在while条件下测试$ line!== falsewhile (($line = stream_get_line($fp, 1024 * 1024, "\n")) !== false)
cebe

8

小心'while(!feof ... fgets()'东西,fgets可能会出错(返回false)并永远循环,而不会到达文件末尾。循环结束,检查feof;如果不正确,则错误。


8

这就是我处理超大文件(测试高达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);

您如何确保1024 * 1024块不会在行中间中断?
user151496

1
@ user151496容易!! 数... 1.2.3.4
奥马尔·埃尔·唐

@OmarElDon是什么意思?
Codex73

7

解决此问题的一种流行方法是换行符出现问题。可以用一个简单的方法很容易地将其修复str_replace

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

6

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

1
<?php
echo '<meta charset="utf-8">';

$k= 1;
$f= 1;
$fp = fopen("texttranslate.txt", "r");
while(!feof($fp)) {
    $contents = '';
    for($i=1;$i<=1500;$i++){
        echo $k.' -- '. fgets($fp) .'<br>';$k++;
        $contents .= fgets($fp);
    }
    echo '<hr>';
    file_put_contents('Split/new_file_'.$f.'.txt', $contents);$f++;
}
?>

-8

带数组返回的读取功能

function read_file($filename = ''){
    $buffer = array();
    $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
    }
    return $buffer;
}

4
这将创建一个内存大于1 GB的单个数组(祝您好运),该数组不仅按行划分,而且分成任意4096个字符块。您到底为什么要这样做?
FrancescoMM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.