Php 如何从一个大的(4GB+;)打开的文件中读取新附加的行?

Php 如何从一个大的(4GB+;)打开的文件中读取新附加的行?,php,Php,使用PHP7.3,我试图实现“tail-f”功能:打开一个文件,等待其他进程写入,然后读取这些新行 不幸的是,fgets()似乎缓存了EOF条件。即使有新数据可用(filemtime更改),fgets()也会返回一个空行 重要的部分:我不能简单地关闭、重新打开然后查找,因为文件大小是几十个Gig,远远高于32位限制。文件必须保持打开状态,才能从正确的位置读取新数据 我附加了一些代码来演示这个问题。如果将数据附加到输入文件,filemtime()会检测到更改,但fgets()不会读取任何新内容 f

使用PHP7.3,我试图实现“tail-f”功能:打开一个文件,等待其他进程写入,然后读取这些新行

不幸的是,fgets()似乎缓存了EOF条件。即使有新数据可用(filemtime更改),fgets()也会返回一个空行

重要的部分:我不能简单地关闭、重新打开然后查找,因为文件大小是几十个Gig,远远高于32位限制。文件必须保持打开状态,才能从正确的位置读取新数据

我附加了一些代码来演示这个问题。如果将数据附加到输入文件,filemtime()会检测到更改,但fgets()不会读取任何新内容

fread()似乎确实有效,可以收集新的数据,但我不想提出自己的“阅读行”解决方案

有人知道我是如何让fgets()意识到这不是EOF的吗

$fn = $argv[1];

$fp = fopen($fn, "r");
fseek($fp, -1000, SEEK_END);
$filemtime = 0;
while (1) {
  if (feof($fp)) {
    echo "got EOF\n";
    sleep(1);
    clearstatcache();
    $tmp = filemtime($fn);
    if ($tmp != $filemtime) {
      echo "time $filemtime -> $tmp\n";
      $filemtime = $tmp;
    }
  }
  $l = trim(fgets($fp, 8192));
  echo "l=$l\n";
}

更新:我尝试排除对feof的调用(认为状态可能在这里被缓存),但行为没有改变;一旦fgets到达原始文件指针位置,任何进一步的fgets读取将返回false,即使随后附加了更多数据


更新2:我最终使用了自己的函数,在达到第一个EOF后将继续返回新数据(事实上,它没有EOF的概念,只是数据可用/数据不可用)。代码没有经过严格测试,因此使用风险自负。希望这对其他人有帮助

define('FGETS_TAIL_CHUNK_SIZE', 4096);
define('FGETS_TAIL_SANITY', 65536);
define('FGETS_TAIL_LINE_SEPARATOR', 10);

function fgets_tail($fp) {

// Get complete line from open file which may have additional data written to it.
// Returns string (including line separator) or FALSE if there is no line available (buffer does not have complete line, or is empty because of EOF)

  global $fgets_tail_buf;

  if (!isset($fgets_tail_buf))  $fgets_tail_buf = "";

  if (strlen($fgets_tail_buf) < FGETS_TAIL_CHUNK_SIZE) { // buffer not full, attempt to append data to it
    $t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
    if ($t != false)  $fgets_tail_buf .= $t;
  }
  $ptr = strpos($fgets_tail_buf, chr(FGETS_TAIL_LINE_SEPARATOR));
  if ($ptr !== false) {
    $rv = substr($fgets_tail_buf, 0, $ptr);                 // includes line separator
    $fgets_tail_buf = substr($fgets_tail_buf, $ptr + 1);    // may reduce buffer to empty
    return($rv);
  } else {
    if (strlen($fgets_tail_buf) < FGETS_TAIL_SANITY) { // line separator not found, try to append some more data
      $t = fread($fp, FGETS_TAIL_CHUNK_SIZE);
      if ($t != false)  $fgets_tail_buf .= $t;
    }
  }
  return(false);
}
define('FGETS\u TAIL\u CHUNK\u SIZE',4096);
定义('FGETS_TAIL_santy',65536);
定义('FGETS\u TAIL\u LINE\u SEPARATOR',10);
函数fgets_tail($fp){
//从打开的文件中获取完整的行,该文件可能会写入其他数据。
//如果没有可用的行(缓冲区没有完整的行,或者由于EOF而为空),则返回字符串(包括行分隔符)或FALSE
全球$fgets\u tail\u buf;
如果(!isset($fgets_tail_buf))$fgets_tail_buf=“”;
如果(strlen($fgets\u tail\u buf)
如果文件大小大于32位,则另一个问题中可能存在的解决方案副本将失败;ftell()将中断。这就是这里的区别。您需要清除静态缓存或禁用静态缓存。还有,为什么你有一个几十个文件大小的单文件,这对大多数文件阅读器来说是一个很大的麻烦打开?!如果您的意思是clearstatcache(),那没有任何区别——上面的代码已经说明了这个问题。