【问题标题】:PHP - Returning the last line in a file?PHP - 返回文件的最后一行?
【发布时间】:2010-11-06 22:57:39
【问题描述】:

我猜是 fgets,但我找不到具体的语法。我正在尝试读出(在我认为更容易的字符串中)添加到日志文件的最后一行。

【问题讨论】:

    标签: php syntax fread fgets


    【解决方案1】:

    您要么必须逐行读取文件并保存最后读取的行以获取它。

    或者如果在 unix/linux 上你可以考虑使用 shell 命令 tail

    tail -n 1 filename
    

    【讨论】:

      【解决方案2】:

      这看起来就是你要找的东西:

      tekkie.flashbit.net: Tail functionality in PHP

      它实现了一个函数,该函数使用带有负索引的 fseek() 从末尾卷起文件。您可以定义要返回的行数。

      代码也可以as a Gist on GitHub:

      // full path to text file
      define("TEXT_FILE", "/home/www/default-error.log");
      // number of lines to read from the end of file
      define("LINES_COUNT", 10);
      
      
      function read_file($file, $lines) {
          //global $fsize;
          $handle = fopen($file, "r");
          $linecounter = $lines;
          $pos = -2;
          $beginning = false;
          $text = array();
          while ($linecounter > 0) {
              $t = " ";
              while ($t != "\n") {
                  if(fseek($handle, $pos, SEEK_END) == -1) {
                      $beginning = true; 
                      break; 
                  }
                  $t = fgetc($handle);
                  $pos --;
              }
              $linecounter --;
              if ($beginning) {
                  rewind($handle);
              }
              $text[$lines-$linecounter-1] = fgets($handle);
              if ($beginning) break;
          }
          fclose ($handle);
          return array_reverse($text);
      }
      
      $fsize = round(filesize(TEXT_FILE)/1024/1024,2);
      
      echo "<strong>".TEXT_FILE."</strong>\n\n";
      echo "File size is {$fsize} megabytes\n\n";
      echo "Last ".LINES_COUNT." lines of the file:\n\n";
      
      $lines = read_file(TEXT_FILE, LINES_COUNT);
      foreach ($lines as $line) {
          echo $line;
      }
      

      【讨论】:

      • Iiinteresting... 但在更简单的方法将问题抛到尾声(除非你真的在一个非常严重过度工作的服务器上)旁边完全没用
      • “完全没用”有点刺耳
      • 好吧,笨蛋,现在你要让我去对它们进行基准测试,不是吗……因为我很好奇……
      • 我也是。所以如果你对它进行基准测试,我会对你的发现感兴趣。 :-) 理想情况下,这将在一个太大而无法放入文件系统缓存的日志文件上进行测试。
      • 非常有用 ;-) 如果您不能依赖 shell 中“tail”的可用性,例如不是 UNIX 或某些共享主机。由于 php 代码有效地使用fseek 仅读取文件尾部,因此性能差异必须是微不足道的。
      【解决方案3】:

      如果您想逐行读取文件,file 函数会逐行读取文件的内容并将每一行作为数组元素返回。

      所以你可以做一些简单的事情,比如:

      $lines    = file('log.txt');
      $lastLine = array_pop($lines);
      

      【讨论】:

      • 真的吗?对于一个多兆字节的日志文件,其中 99.99% 是我不感兴趣的,我会尽量避免将其全部加载到一个数组中,只是为了立即将其丢弃。
      • 不可否认,这是低效的,但确实有效;谁知道文件有多长?我会在我的环境中使用 tail 命令,但 WiseDonkey 没有指定任何命令。不过,这是您链接到的一个不错的功能。
      • file() 和 file_get_contents() 都是很棒的文件操作函数,特别是如果您知道所涉及的文件相对较小并且只想快速轻松地做某事。
      • @Matthew Scharley:OP 谈到了日志文件。大多数情况下,这些与“相对较小”相反。
      【解决方案4】:

      最简单的幼稚解决方案就是:

      $file = "/path/to/file";
      $data = file($file);
      $line = $data[count($data)-1];
      

      不过,这会将整个文件加载到内存中。可能是一个问题(或不是)。更好的解决方案是:

      $file = escapeshellarg($file); // for the security concious (should be everyone!)
      $line = `tail -n 1 $file`;
      

      【讨论】:

      • 请注意,除非您使用 escapeshellarg(),否则这是非常不安全的:de.php.net/manual/en/function.escapeshellarg.php
      • 更正,如果您接受用户输入作为文件路径(然后您应该做各种事情来验证它),这只是不安全的。如果您使用示例中的常量,那么它非常好,您可以为自己节省一个函数调用。当然,无论如何,这可能不是一个坏主意,只是为了养成习惯。
      • 进一步扩展您的评论(“如果您在 Mac 上...?”) - 如果您在 Windows 上并且没有“tail”命令怎么办? (我知道有一个 Win32 端口的“tail”,这是题外话)。除此之外 - 即使我没有测试过它,如果为每个请求创建一个新流程不能很好地扩展,我也不会感到惊讶。
      • 如果您在escapeshellarg 示例中包含所需的其余代码,那就太好了。创建一个字符串不会神奇地得到任何东西的最后一行。
      • @carter 不;到tail 的外壳就是这样做的。可悲的是,生活中没有什么是神奇的。
      【解决方案5】:
      define('YOUR_EOL', "\n");
      $fp = fopen('yourfile.txt', 'r');
      
      $pos = -1; $line = ''; $c = '';
      do {
          $line = $c . $line;
          fseek($fp, $pos--, SEEK_END);
          $c = fgetc($fp);
      } while ($c != YOUR_EOL);
      
      echo $line;
      
      fclose($fp);
      

      这样更好,因为它不会将完整的文件加载到内存中......

      将 YOUR_EOL 设置为正确的行尾,如果您使用与脚本所在操作系统的默认行尾相同的行尾,则可以使用常量 PHP_EOL。

      【讨论】:

        【解决方案6】:

        这不会因为 1 行或 0 行文件而中断。

        function readlastline($fileName)
        {
        
               $fp = @fopen($fileName, "r");
               $begining = fseek($fp, 0);      
               $pos = -1;
               $t = " ";
               while ($t != "\n") {
                     fseek($fp, $pos, SEEK_END);
                     if(ftell($fp) == $begining){
                      break;
                     }
                     $t = fgetc($fp);
                     $pos = $pos - 1;
               }
               $t = fgets($fp);
               fclose($fp);
               return $t;
        }
        

        【讨论】:

          【解决方案7】:
          function seekLastLine($f) {
              $pos = -2;
              do {
                  fseek($f, $pos--, SEEK_END);
                  $ch = fgetc($f);
              } while ($ch != "\n");
          }
          

          -2 因为最后一个字符可以是\n

          【讨论】:

            【解决方案8】:

            ...为什么只读最后一行?

            function readLines($fp, $num) {
            
                $line_count = 0; $line = ''; $pos = -1; $lines = array(); $c = '';
            
                while($line_count < $num) {
                    $line = $c . $line; 
                    fseek($fp, $pos--, SEEK_END);
                    $c = fgetc($fp);
                    if($c == "\n") { $line_count++; $lines[] = $line; $line = ''; $c = ''; }
                }   
                return $lines;
            }
            
            $filename = "content.txt";
            $fp = @fopen($filename, "r");
            
            print_r(readLines($fp, 2));
            
            fclose($fp);
            

            【讨论】:

              【解决方案9】:

              @unique_stephen,你的回答有问题。 PHP fseek 成功返回 0,失败返回 -1。将结果存储在 $begining (sic) 中,然后在 ftell() 的过滤器中使用它是不正确的。如果我的声誉更高,我会投票给你并留下评论。这是 unique_stephen 函数的修改版本。

              function readlastline($fileName)
              {
                  $fp = @fopen($fileName, "r");
                  if (fseek($fp, 0) == -1)
                      exit('Cannot seek to beginning of the file'); 
                  $pos = -1;
                  $t = " ";
                  while ($t != "\n") {
                      if (fseek($fp, $pos, SEEK_END) == -1)
                          exit('Cannot seek to the end of the file');
                      if (ftell($fp) == 0) {
                          break;
                      }
                      $t = fgetc($fp);
                      $pos = $pos - 1;
                  }
                  $t = fgets($fp);
                  fclose($fp);
                  return $t;
              }
              

              注意:PHP 的 fseek 无法设法找到大于 PHP_MAX_INT 的文件的末尾,即使在 64 位二进制文​​件上也是 32 位签名。

              【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-01-15
              • 2018-05-24
              • 1970-01-01
              相关资源
              最近更新 更多