【问题标题】:Writing a huge file in most efficient way以最有效的方式编写大文件
【发布时间】:2018-05-31 11:38:13
【问题描述】:

我正在尝试用 php 编写一个 50+ MB 范围内的文件。

这有点像预期的那样工作,虽然速度很慢。

运行起来很简单:

$fileAccess = fopen($filename, 'w');
fwrite($fileAccess, $line);
*a lot of lines and loops...*
fclose($fileAccess);

我的问题是。我可以做些什么来优化它。 我向文件发送了大约 350000 个 fwrite 大约 100-10000 个字符的语句,我想知道是否可以做些什么来提高文件生成的效率。 是做所有这些小写更好,还是我应该在写之前在内部“缓存”一些内容,还是有第三种我不知道的选项。

我必须降低内存消耗,否则我会遇到服务器限制。

谢谢

【问题讨论】:

  • a lot of lines and loops 什么也没告诉我。你可以在那里写 35000 行,这将相当快。您还可以循环遍历 35000 个数组,这绝对会造成破坏。此外,强制性 JoelOnSoftware:joelonsoftware.com/2001/12/11/back-to-basics

标签: php


【解决方案1】:

这可能对你有帮助

// Copy big file from somewhere else
$src_filepath = 'http://example.com/all_the_things.txt'; $src = fopen($src_filepath, 'r');
$tmp_filepath = '...'; $tmp = fopen($tmp_filepath, 'w');
$buffer_size = 1024;

while (!feof($src)) {                       
   $buffer = fread($src, $buffer_size);     // Read big file/data source/etc. in small chunks
   fwrite($tmp, $buffer);                   // Write in small chunks
}

fclose($tmp_filepath);                      // Clean up
fclose($src_filepath);

rename($tmp_filepath, '/final/path/to/file.txt');

【讨论】:

    【解决方案2】:

    而不是使用许多文件写入的开销,您可以只使用一个,例如,我使用一个数组,我在末尾用新行内爆来写入;

    <?php
    
    $filecontent = array();
    $handle = fopen($filename, "w");
    
    while ($x == $y)
    {
        if ($condition_met)
        {
            $filecontent[] = "Some message to say this worked";
        }
        if ($condition_met)
        {
            $filecontent[] = "Some message to say this failed";
        }
    }
    
    $filecontent = implode("\r\n", $filecontent);
    fwrite($handle, $filecontent);
    fclose($file);
    

    这意味着您已经为句柄打开了资源,以及要添加的值数组,只需 implode 和 write 一次通常对我有用

    编辑

    如果你的内存过度使用,仍然使用数组,你可以在最后循环,以避免一直写写,但你仍然会看到同样的性能损失,我试图减少这通过添加一个计数器来减少写入频率;

    $filecontent = array();
    $handle = fopen($filename, "w");
    
    while ($x == $y)
    {
        if ($condition_met)
        {
            $filecontent[] = "Some message to say this worked";
        }
    }
    $counts = 0;
    $addtofile = "";
    foreach ($filecontent as $addline)
    {
        if ($counts < 2500)
        {
            $addtofile .= $addline . "\r\n";
            $counts++;
        }
        else
        {
            fwrite($handle, $addtofile);
            $addtofile = "";
            $counts = 0;
        }
    }
    fclose($file);
    

    【讨论】:

    • 这和我之前做的很相似。问题是它消耗了很多内存。因此我转而写单行,但这似乎在服务器站点上产生了巨大的开销,写了那么多行。
    • 我将添加第二种方法 - 挂火
    • @DennisC - 添加了编辑和一个通过将单次写入限制为 2500 行(显然可以更改)而有望提高性能的编辑
    • 使用了你的变体(因为一些递归循环)服务器上花费的时间减少了 90%
    • 10 分钟到 1 分钟左右,是一个不错的增长 :) 是的。可能会进一步优化它。 (似乎总是对任何代码进行另一层优化)
    【解决方案3】:

    使用file_put_contents。写入文件是这里最慢的部分,所以尽量少用:

    $content = '';
    $threshold = 1000;
    $handler = fopen('my_file.txt', 'a');
    
    foreach ($contents as $i => $data) {
        $content .= $data;
    
        // Write in batches
        if ($i > $threshold) {
            fwrite($handler, $content);
            $content = ''; // Reset content
        }
    }
    
    // Write what's left in $content
    if (!empty($content)) {
        fwrite($handler, $content);
    }
    
    fclose($handler);
    

    【讨论】:

    • 如果file_put_contents() 的开销(即打开文件、写入、关闭文件)比仅将块写入fopen() 文件更好。
    • @NigelRen 我也很想知道对文件 put 或 fwrite 进行许多较小的调用与仅使用较大的连接字符串进行比较
    • 一个fopen和fclose不是更好,然后使用你描述的。在将其推送到文件之前组合一定数量的行。
    猜你喜欢
    • 2017-01-19
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 2023-03-24
    相关资源
    最近更新 更多