【问题标题】:Fastest way possible to read contents of a file读取文件内容的最快方法
【发布时间】:2010-05-01 09:36:00
【问题描述】:

好的,我正在寻找最快的方法来通过 php 读取文件的所有内容,并在服务器上使用文件路径,这些文件也可能很大。因此,尽可能快地对其执行只读操作非常重要。

逐行阅读是否比阅读全部内容更快?不过,我记得读过一些,阅读全部内容可能会导致大文件出错。这是真的吗?

【问题讨论】:

  • 这个问题有点老了,但为了以后参考,我前段时间找到了this site。它对几种 PHP 读取方法进行了基准测试,并得出结论 readfile()fpassthru 是最快的,只要您需要对该文件进行零处理(即文件中没有需要处理的 PHP 脚本)。
  • 这里有几个重要的PHP获取内容的方法,在启动函数之前用echo microtime测试它们,在函数之后再做echo microtime看看结果:stackoverflow.com/questions/2176180/…

标签: php file-io


【解决方案1】:

如果您想将文件的全部内容加载到 PHP 变量中,最简单的(并且可能是最快的)方式file_get_contents

但是,如果您正在处理大文件,将整个文件加载到内存中可能不是一个好主意:您可能最终会遇到 memory_limit 错误,因为 PHP 不允许您的脚本使用更多比 (通常) 几兆字节的内存。


因此,即使它不是最快的解决方案,也可以逐行读取文件(fopen+fgets+fclose),并即时处理这些行,而无需加载整个文件文件到内存中,可能需要...

【讨论】:

  • 使用SESSIONS 存储此信息是否是个好主意,这样我们就不必继续打开文件了,如果它已经打开过一次?
  • 首先,会话(默认)存储到文件中;;;那么,您不应该将大数据放入会话 (因为它对每个请求都进行了序列化/反序列化) ;;;并将其存储到会话将复制数据:每个用户都有不同的会话;;;所以,我会说不,将其存储到会话不是一个好主意。
  • 所以,抱歉,如果我不理解这一点,您认为在逐行读取文件之后将其作为序列化字符串存储到数据库中会更好吗?只是通过反序列化来打开它?
  • 只要你尝试将整个文件加载到内存中(无论是从文件中,从会话中,从数据库中),如果数据太长,它会消耗到很多内存;;;这就是为什么不使用太多内存的最佳解决方案是逐行读取文件,在读取时直接处理每一行,而不是将整个数据存储到内存中。
【解决方案2】:

file_get_contents() 是在 PHP 中读取文件的最优化方式,但是 - 因为您是在内存中读取文件您总是受限于可用的内存量

如果您拥有正确的权限,您可以发出ini_set('memory_limit', -1),但您仍然会受到系统上可用内存量的限制,这对所有编程语言都很常见。

唯一的解决方案是分块读取文件,您可以使用file_get_contents() 和第四个和第五个参数($offset$maxlen - 以字节指定):

string file_get_contents(string $filename[, bool $use_include_path = false[, resource $context[, int $offset = -1[, int $maxlen = -1]]]])

这是我使用此技术提供大型下载文件的示例:

public function Download($path, $speed = null)
{
    if (is_file($path) === true)
    {
        set_time_limit(0);

        while (ob_get_level() > 0)
        {
            ob_end_clean();
        }

        $size = sprintf('%u', filesize($path));
        $speed = (is_int($speed) === true) ? $size : intval($speed) * 1024;

        header('Expires: 0');
        header('Pragma: public');
        header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
        header('Content-Type: application/octet-stream');
        header('Content-Length: ' . $size);
        header('Content-Disposition: attachment; filename="' . basename($path) . '"');
        header('Content-Transfer-Encoding: binary');

        for ($i = 0; $i <= $size; $i = $i + $speed)
        {
            ph()->HTTP->Flush(file_get_contents($path, false, null, $i, $speed));
            ph()->HTTP->Sleep(1);
        }

        exit();
    }

    return false;
}

另一种选择是使用优化程度较低的 fopen()feof()fgets()fclose() 函数,特别是如果您想一次获得整行,这里是 @987654321 @:

function SplitSQL($file, $delimiter = ';')
{
    set_time_limit(0);

    if (is_file($file) === true)
    {
        $file = fopen($file, 'r');

        if (is_resource($file) === true)
        {
            $query = array();

            while (feof($file) === false)
            {
                $query[] = fgets($file);

                if (preg_match('~' . preg_quote($delimiter, '~') . '\s*$~iS', end($query)) === 1)
                {
                    $query = trim(implode('', $query));

                    if (mysql_query($query) === false)
                    {
                        echo '<h3>ERROR: ' . $query . '</h3>' . "\n";
                    }

                    else
                    {
                        echo '<h3>SUCCESS: ' . $query . '</h3>' . "\n";
                    }

                    while (ob_get_level() > 0)
                    {
                        ob_end_flush();
                    }

                    flush();
                }

                if (is_string($query) === true)
                {
                    $query = array();
                }
            }

            return fclose($file);
        }
    }

    return false;
}

您使用哪种技术实际上取决于您要执行的操作(正如您在 SQL 导入功能和下载功能中看到的那样),但 您总是必须以块的形式读取数据。

【讨论】:

    【解决方案3】:
    $file_handle = fopen("myfile", "r");
    while (!feof($file_handle)) {
       $line = fgets($file_handle);
       echo $line;
    }
    fclose($file_handle);
    
    1. 打开文件并将其存储在$file_handle 中作为对文件本身的引用。
    2. 检查您是否已经在文件末尾。
    3. 继续阅读文件,直到读完为止,在阅读时打印每一行。
    4. 关闭文件。

    【讨论】:

    • 如果文件的行很短,一次读取一行可能不是最佳选择。读取特定大小的块可能会更好
    • 关于feof():如果文件无法读取或不存在,fopen 函数返回 FALSE。 fopen 的 FALSE 将发出警告并在此处导致无限循环。最好检查 fgets 不是 FALSE,因为:while (($line = fgets($file_handle)) !== false)
    【解决方案4】:

    你可以使用 file_get_contents

    示例:

    $homepage = file_get_contents('http://www.example.com/');
    echo $homepage;
    

    【讨论】:

    • 所以这适用于任何大小的文件?不管文件大小有多大?
    • @SoLoGHoST:不,它也有内存限制。
    • 哦,好吧,比这不是我想要的比。无论如何,谢谢。
    • @SoLoGHoST:作为替代方案,您可以逐行使用fgets 函数。此外,file_get_contents 函数在大多数情况下都能正常工作。
    • 上述代码只有在指令allow_url_fopenphp.ini文件中设置为On1时才有效。
    【解决方案5】:

    使用 fpassthru 或 readfile。 随着文件大小的增加,两者都使用常量内存。

    http://raditha.com/wiki/Readfile_vs_include

    【讨论】:

      【解决方案6】:
      foreach (new SplFileObject($filepath) as $lineNumber => $lineContent) {
      
          echo $lineNumber."==>".$lineContent;  
          //process your operations here
      }
      

      【讨论】:

        【解决方案7】:

        一次性读取整个文件更快。

        但是大文件可能会占用您所有的内存并导致问题。那么你最安全的选择就是逐行阅读。

        【讨论】:

          【解决方案8】:

          如果您不担心内存和文件大小,

          $lines = file($path);
          

          $lines 是文件的数组。

          【讨论】:

          • 我认为这只是一个 preg_split(/\r?\n?/, file_get_contents())
          【解决方案9】:

          你可以试试 cURL (http://php.net/manual/en/book.curl.php)。

          尽管您可能想要检查,但它也有其局限性

          $ch = curl_init();
          curl_setopt($ch, CURLOPT_URL, "http://example.com/");
          curl_setopt ($ch, CURLOPT_RETURNTRANSFER, 1);
          $data = curl_exec ($ch); // Whole Page As String
          curl_close ($ch);
          

          【讨论】:

          • 每个单词的首字母大写有什么原因吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-08-26
          • 2017-12-31
          • 2019-07-31
          • 1970-01-01
          • 2016-06-16
          相关资源
          最近更新 更多