fseek

(PHP 4,PHP 5,PHP 7,PHP 8)

fseek在文件指针上查找

描述

fseek(资源 $stream, 整数 $offset, 整数 $whence = SEEK_SET): 整数

设置 stream 引用的文件的 file position indicator。新位置(以字节为单位,从文件开头开始测量)通过将 offset 添加到 whence 指定的位置获得。

通常情况下,允许在文件的结尾处进行 seek;如果之后写入数据,在文件结尾与所查找位置之间的任何未写入区域中的读取将生成值 0 的字节。但是,某些流可能不支持此行为,特别是当它们具有底层固定大小的存储时。

参数

stream

使用 fopen() 生成的文件系统指针 资源

offset

偏移量。

要移动到文件结束前的位置,需要在 offset 中传入一个负值并将 whence 设置为 SEEK_END

whence

whence 的值:

  • SEEK_SET - 将位置设置为等于 offset 个字节。
  • SEEK_CUR - 将位置设置为当前位置加 offset
  • SEEK_END - 将位置设置为文件结尾加 offset

返回值

成功时返回 0;否则返回 -1。

示例

示例 1 fseek() 示例

<?php

$fp
= fopen('somefile.txt', 'r');

// 读取一些数据
$data = fgets($fp, 4096);

// 返回到文件开头
// 相同于 rewind($fp);
fseek($fp, 0);

?>

注释

请注意:

如果您已在追加(aa+)模式下打开文件,则无论文件位置如何,您写入文件中的任何数据都总会附加,并且调用 fseek() 的结果将是未定义的。

请注意:

并非所有流都支持查找。对于不支持查找的流,可以通过读取和丢弃数据来从当前位置向后查找;其他形式的查找将失败。

另请参见

  • ftell() - 返回文件读/写指针的当前位置
  • rewind() - 倒回文件指针的位置

添加注释

用户贡献的注释 25 条注释

seeker at example com
16 年前
仅引用和指明这一点

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3.如果您使用 fseek() 向文件写入数据,请记住以“r+”模式打开文件
模式,例如

$fp=fopen($filename,"r+");

不要以“a”(追加)模式打开文件,因为它将
将文件指针置于文件的末尾,并且不允许您
fseek 文件中的早期位置(对我而言没有这样做!)。另外,
不要以“w”模式打开文件 -- 尽管这将使您处于
文件的开头 -- 因为它会清除中所有数据
该文件。

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

花了半天时间才弄清楚 :/
匿名
13 年前
官方文档表明并非所有流都是可查找的。
您可以尝试查找并处理失败

<?php
if (fseek($stream, $offset, SEEK_CUR) === -1) {
// 无论如何
}
?>

或者,您可以使用 stream_get_meta_data 函数
https://php.net/stream_get_meta_data

<?php
function fseekable($stream) {
$meta = stream_get_meta_data($stream);
return
$meta['seekable'];
}
?>
lorenzo dot stanco at gmail dot com
11 年前
我想要对“从文件中读取最后几行”这个主题贡献我的力量。我做了一些研究(实际上就是从这里开始的),并且针对不同的算法和场景进行了许多测试,得到了以下结论

在 PHP 中从文件中读取最后几行的最佳方法是什么?
http://stackoverflow.com/a/15025877/995958

在这篇小文章中,我尝试分析所有不同的方法以及它们对于不同文件类型的性能。

希望有所帮助。
marc dot roe at gmail dot com
17 年前
我尝试改进和修改了 (mail at ulf-kosack dot de) 的函数。实际上速度非常快,即比使用 file() 或 file_get_contents() 来获取文件的最后五、十行或任意行要快得多。

函数 read_file($file, $lines)
{
$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); // array_reverse 是可选的:你也可以只返回由文件行组成的 $text 数组。
}

现在的好处是,即使你请求的文件行数多于实际文件行数,也不会出现错误。在这种情况下函数只会返回整个文件内容。
synnus at gmail dot com
10 年前
用 Php 32 位 (X86) 编写 4 GB 的虚拟文件
如果你想编写大于 4GB 的文件,请使用 Php(X64)。
此文件在 0.0041329860687256 秒内创建

CreatFileDummy('data_test.txt',4294967296);

函数 CreatFileDummy($file_name,$size) {
// 32 位 4 294 967 296 字节最大尺寸
$f = fopen($file_name, 'wb');
if($size >= 1000000000) {
$z = ($size / 1000000000);
if (is_float($z)) {
$z = round($z,0);
fseek($f, ( $size - ($z * 1000000000) -1 ), SEEK_END);
fwrite($f, "\0");
}
while(--$z > -1) {
fseek($f, 999999999, SEEK_END);
fwrite($f, "\0");
}
}
else {
fseek($f, $size - 1, SEEK_END);
fwrite($f, "\0");
}
fclose($f);

返回真;
}

Synx
匿名
16 年前
发给:seeker at example com
但是请小心。
如果你以 (r+) 模式打开一个文件,你可以自由地定位你的指针,但它会“覆盖”数据,而不是“追加它”。

我试过了

<?php
// file.txt 内容:
// "You can contribute your notes to the PHP manual from the comfort of your browser!"

$handler = fopen("file.txt", "r+");
fseek($handler, 0);
fwrite($handler, "want to add this");
?>
file.txt 的新内容如下
"want to add thiste your notes to the PHP manual from the comfort of your browser!".

如果你真的想追加在开头,你必须先获取所有内容,保存它,清空文件,放入新内容,然后在结尾追加保存的内容。
Lutz ( l_broedel at gmx dot net )
19 年前
基于以下由 info at o08 dot com 提供的函数(感谢),你可以用它从一个文件读取一行,由行号(从 1 开始)标识

<?
function readLine ($linenum,$fh) {
$line = fgets ($fh, 4096);
$pos = -1;
$i = 0;

while (!feof($fh) && $i<($linenum-1)) {
$char = fgetc($fh);
if ($char != "\n" && $char != "\r") {
fseek($fh, $pos, SEEK_SET);
$pos ++;
}
else $i ++;
}
$line = fgets($fh);
return $line;
} //readLine()
?>
Anonymous
21 年前
不要对可能被并行进程或线程访问和更新的文件使用 filesize()(因为 filesize() 的返回值会保留在缓存中)。
相反,锁定打开的文件,并使用 fseek($fp,0,SEEK_END) 和 ftell($fp) 来获取实际文件大小,如果你需要执行 fread() 调用以读取整个文件...
me at php dot net
11 年前
如何使用 fseek 读取大文件(超过 2GB+,任意大小,例如 4GB+、100GB+、100 万亿字节+、任何文件大小、100 亿亿字节、最大限制是 php_float_max)?

// 将文件指针设置为 50 GB
my_fseek($fp, floatval(50000000000),1);

function my_fseek($fp,$pos,$first=0) {

// 最初将 pos 设置为 0,一次性
if($first) fseek($fp,0,SEEK_SET);

// 获取 pos 浮点数
$pos=floatval($pos);

// 在限制范围内,使用常规 fseek
if($pos<=PHP_INT_MAX)
fseek($fp,$pos,SEEK_CUR);
// 超出限制,使用递归 fseek
else {
fseek($fp,PHP_INT_MAX,SEEK_CUR);
$pos -= PHP_INT_MAX;
my_fseek($fp,$pos);
}

}

希望这有帮助。
汤姆·皮特利克
15 年前
下面的 tail 示例函数在尝试打开大型文件时会返回 PHP 内存限制错误。由于 tail 便于打开大型日志,因此这里有一个函数可以让您执行此操作(前提是您有权限)

<?php

function unix_tail($lines,$file)
{
shell_exec("tail -n $lines $file > /tmp/phptail_$file");
$output = file_get_contents("/tmp/phptail_$file");
unlink("/tmp/phptail_$file");
return
$output;
}

?>
kavoshgar3 at gmail dot com
12 年前
有时我们需要从文件的最后一行读取到文件开头。我使用以下代码。
<?php
function read_backward_line($filename, $lines, $revers = false)
{
$offset = -1;
$c = '';
$read = '';
$i = 0;
$fp = @fopen($filename, "r");
while(
$lines && fseek($fp, $offset, SEEK_END) >= 0 ) {
$c = fgetc($fp);
if(
$c == "\n" || $c == "\r"){
$lines--;
if(
$revers ){
$read[$i] = strrev($read[$i]);
$i++;
}
}
if(
$revers ) $read[$i] .= $c;
else
$read .= $c;
$offset--;
}
fclose ($fp);
if(
$revers ){
if(
$read[$i] == "\n" || $read[$i] == "\r")
array_pop($read);
else
$read[$i] = strrev($read[$i]);
return
implode('',$read);
}
return
strrev(rtrim($read,"\n\r"));
}
//if $revers=false function return->
//line 1000: i am line of 1000
//line 1001: and i am line of 1001
//line 1002: and i am last line
//but if $revers=true function return->
//line 1002: and i am last line
//line 1001: and i am line of 1001
//line 1000: i am line of 1000
?>
Enjoy! Mail me if it works! ;-)
marin at sagovac dot com
8 年前
查找一行代码比休息优化性能。使用 SEEK_SET 查找特定行并获取特定行。如果 $range 为“0”,那么将显示已查找的行。如果设置为“2”,那么它将显示当前行 + 上面 2 行 + 下面 2 行。

这有助于在非常大的文件中获取文件的内容以获取行范围。为了提高性能,一个 while 循环会打破迭代而不是进行查找。

我创建了一个函数来读取文件和计数行,并将每行的字节存储到数组中以进行查找。如果由 `linenum` 指定的最大值被设置为中断 while 以保持性能,则在一个新的循环函数中查找字节中的位置以获取文件的内容。

function readFileSeek($source, $linenum = 0, $range = 0)
{
$fh = fopen($source, 'r');
$meta = stream_get_meta_data($fh);

if (!$meta['seekable']) {
throw new Exception(sprintf("源不可查找: %s", print_r($source, true)));
}

$pos = 2;
$result = null;

if ($linenum) {
$minline = $linenum - $range - 1;
$maxline = $minline+$range+$range;
}

$totalLines = 0;
while (!feof($fh)) {

$char = fgetc($fh);

if ($char == "\n" || $char == "\r") {
++$totalLines;
} else {
$result[$totalLines] = $pos;
}
$pos++;

if ($maxline+1 == $totalLines) {
从 while 中断以免读取整个文件
中断;
}
}

$buffer = '';

for ($nr=$minline; $nr<=$maxline; $nr++) {

if (isset($result[$nr])) {

fseek($fh, $result[$nr], SEEK_SET);

while (!feof($fh)) {
$char = fgetc($fh);

if ($char == "\n" || $char == "\r") {
$buffer .= $char;
中断;
} else {
$buffer .= $char;
}
}

}
}

return $buffer;
}

测试结果(文件大小为 1.3 GB,代码行数为 100000000,从中获取第 300000 行代码)

string(55) "299998_abc
299999_abc
300000_abc
300001_abc
300002_abc
"

耗时:612 毫秒,内存使用量:20.00Mb

$ ll -h /tmp/testfile
-rw-rw-r-- 1 1,3G /tmp/testfile
chenganeyou at eyou dot com
19 年前
我使用以下代码来读取文件的最后一行。
与 jim at lfchosting dot com 相比,这应当更加高效。

<?php
function readlastline($file)
{
$linecontent = " ";
$contents = file($file);
$linenumber = sizeof($file)-1;
$linecontet = $contents[$linenumber];
unset(
$contents,$linenumber);
return
$linecontent;
}
?>
ben at nullcreations dot net
15 年前
简易 tail() 函数,用于 PHP

<?php
函数 tail($file, $num_to_get=10)
{
$fp = fopen($file, 'r');
$position = filesize($file);
fseek($fp, $position-1);
$chunklen = 4096;
while(
$position >= 0)
{
$position = $position - $chunklen;
if (
$position < 0) { $chunklen = abs($position); $position=0;}
fseek($fp, $position);
$data = fread($fp, $chunklen). $data;
if (
substr_count($data, "\n") >= $num_to_get + 1)
{
preg_match("!(.*?\n){".($num_to_get-1)."}$!", $data, $match);
return
$match[0];
}
}
fclose($fp);
return
$data;
}
?>
alan at peaceconstitution.com
19 年前
感谢 Dan,他的以上评论提供了解决如何向文件追加内容问题的一个关键。
接着,使用 phpinfo(); 我确保按照 fopen() 手册条目中提到的文本对我的 PHP 安装进行了必要的设置,我很疑惑,为什么我的 fopen() 使用追加选项“a”(追加选项)不起作用。然后我读到了为附录 L 做出的注释 (http://us2.php.net/manual/en/wrappers.php) fopen() 的追加选项“a”不能按预期工作。作者建议改用“w”选项,我发现此选项有效。但“w”选项(写入选项)会覆盖文件中的所有内容。
问题仍然是,如何完成追加。按照 Dan 对“r+”选项的建议,我尝试了此方法,它工作得很好
$string = "要写入日志的消息";
$filehandle = fopen ("/home/name/sqllogs/phpsqlerr.txt", 'r+');
fseek ( $filehandle,0, SEEK_END);
fwrite ( $filehandle, $string."\n" );
fclose ($filehandle);
ekow[at]te.ugm.ac.id
18 年前
chenganeyou at eyou dot com 用于读取最后一行代码的小小更正。
$linenumber = sizeof($file)-1;
应该是
$linenumber = sizeof($contents)-1;
因为 sizeof 将计算数组元素,而不是文件大小。
<?php
function readlastline($file)
{
$linecontent = " ";
$contents = file($file);
$linenumber = sizeof($contents)-1;
$linecontet = $contents[$linenumber];
unset(
$contents,$linenumber);
return
$linecontent;
}
?>
phil at NOSPAM dot blisswebhosting dot com
19 年前
为了从后向前阅读文本文件,例如首先显示日志文件的最新内容,我使用以下方法。

它基本上使用 fseek 找到文件结尾,使用 ftell 找到用于计数的字节数,然后使用 fgetc 向后迭代浏览文件以测试换行符。

$i=0 ;
$lines=500 ;
$fp = fopen($log,"r") ;
if(is_resource($fp)){
fseek($fp,0,SEEK_END) ;
$a = ftell($fp) ;
while($i <= $lines){
if(fgetc($fp) == "\n"){
echo (fgets($fp));
$i++ ;
}
fseek($fp,$a) ;
$a-- ;
}
}
jim at lfchosting dot com
20 年前
这是一个返回文件最后一行 的函数,速度应该比读取整个文件直到获取最后一行更快。如果想要加快速度,可以将 $pos = 某数字 设置为比行长再大一些。我要处理的文件长度各不相同,所以此方法对我行之有效。

<?php
function readlastline($file)
{
$fp = @fopen($file, "r");
$pos = -1;
$t = " ";
while (
$t != "\n") {
fseek($fp, $pos, SEEK_END);
$t = fgetc($fp);
$pos = $pos - 1;
}
$t = fgets($fp);
fclose($fp);
return
$t;
}
?>
匿名
12 年前
我需要流式传输一个 txt 文件(大型 xml 文件),以按块方式获取节点。找不到更短的方法,于是我编写了这个类。

函数:流式传输完成文件,并返回两个搜索字符串及其搜索字符串之间的内容(多字节安全)

希望它对任何人都有帮助。

PS:它缺少任何针对不存在的文件/读取错误的布尔值检查/异常处理。

<?php
/**
* Reads txt-files blockwise
* Usage:
$c_streamFileTxt = new streamFileTxt;
$_args = array(

'file' => 'temporary.xml',
'start_string' => '<Product>',
'stop_string' => '</Product>',
'block_size' => '8192'
);

$c_streamFileTxt->setArgs($_args);

while ($txt_block = $c_streamFileTxt->getNextBlock())
{
// use $txt_block for something
}
*/

class streamFileTxt
{
private
$handle;
private
$file;
private
$file_offset;
private
$block;
private
$start_string;
private
$stop_string;
private
$block_size;

/**
* Sett class arguments
* @param array $_args
*/
public function setArgs($_args)
{
$this->file = $_args['file'];
$this->start_string = $_args['start_string'];
$this->stop_string = $_args['stop_string'];
$this->block_size = $_args['block_size'];
}

/**
* Get next textblock within a file
* @param void
* @return string $textblock
*/
public function getNextBlock()
{
$this->openFile();

fseek($this->handle, $this->file_offset);
$start_string_found = false;
$stop_string_found = false;
while (!
feof($this->handle))
{
$txt_block = fread($this->handle, $this->block_size);

if (!
$start_string_found) // while not start start snippet found
{
$strpos = mb_strpos($txt_block, $this->start_string);
if (
$strpos !== false)
{
// cut of first left chunk
$txt_block = mb_substr($txt_block, $strpos, $this->block_size);
$start_string_found = true;
}
}

if (
$start_string_found && !$stop_string_found) // start snipped found, looking for stop snippet
{
$strpos = mb_strpos($txt_block, $this->stop_string);
if (
$strpos !== false)
{
$removed_block_size = mb_strlen($txt_block) - $strpos;
$txt_block = mb_substr($txt_block, 0, $strpos + mb_strlen($this->stop_string));
$stop_string_found = true;
$this->setFileOffset($removed_block_size);
}
}

if (
$stop_string_found) // stop-snippet found, keep file offset, return
{
$this->closeFile();
return
$txt_block;
}
}

$this->closeFile();
return
false;
}

/**
* Set current file offset and consider the removed block size
* current file position = current file offset - removed block size
* @param int $removed_block_size
*/
private function setFileOffset($removed_block_size)
{
$this->file_offset = ftell($this->handle) - $removed_block_size;
}

/**
* close current file
* @param void
* @return void
*/
private function openFile()
{
$this->handle = fopen($this->file, 'r');
}

/**
* open file
* @param void
* @return void
*/
private function closeFile()
{
fclose($this->handle);
}
}
lexica98 at gmail dot com
10 年前
遗憾的是,使用 a+ 模式打开文件同样无法使用 fseek。如果你想创建一个文件,然后希望能够访问文件中的任何位置,你必须在打开文件时使用追加模式,然后关闭文件,再以 r+ 方式再次打开文件。
necudeco at gmail dot com
14 年前
这是一个适用于 Windows 系统的 php 尾部脚本示例。

<?php
$n
= ( isset($_REQUEST['n']) == true )? $_REQUEST['n']:20;

$offset = -$n * 120;

$rs = fopen('C:/wamp/logs/apache_error.log','r');
if (
$rs === false )
die(
"No se pudo abrir el archivo de log");

fseek($rs,$offset,SEEK_END);

fgets($rs);
while(!
feof($rs))
{
$buffer = fgets($rs);
echo
$buffer;
echo
"<hr />";
}

fclose($rs);
?>
lucky at somnius dot com dot ar
17 年前
如果文件不为空,或者有不止一行,则使用 Jim(jim at lfchosting dot com)的最后一行问题代码非常完美。但是,如果你使用的是不包含任何换行符的文件(即为空白,或者只有一行且仅一行),则 while 循环会无限进行。

我知道此脚本适用于总是包含至少几行的超大文件,但如果使该脚本防错那就很明智。

因此,对他的代码进行了一点修改。

<?php
函数 readLastLine ($file) {
$fp = @fopen($file, "r");

$pos = -1;
$t = " ";
while (
$t != "\n") {
if (!
fseek($fp, $pos, SEEK_END)) { // *** - fseek 返回 0 表示成功,-1 表示失败,如在文件范围之外查找字节
$t = fgetc($fp);
$pos = $pos - 1;
} else {
// ***
rewind($fp); // ***
break; // ***
} // ***
}
$t = fgets($fp);
fclose($fp);
return
$t;
}
?>

已使用 "// ***" 标记已添加和/或已修改的行。希望有所帮助!

问候!
steve at studio831 dot com
15 年前
已修改 @ben 的函数以适用于大于 PHP_INT_MAX 字节的文件。

<?php
function longTail($file, $numLines = 100)
{
$fp = fopen($file, "r");
$chunk = 4096;
$fs = sprintf("%u", filesize($file));
$max = (intval($fs) == PHP_INT_MAX) ? PHP_INT_MAX : filesize($file);

for (
$len = 0; $len < $max; $len += $chunk) {
$seekSize = ($max - $len > $chunk) ? $chunk : $max - $len;

fseek($fp, ($len + $seekSize) * -1, SEEK_END);
$data = fread($fp, $seekSize) . $data;

if (
substr_count($data, "\n") >= $numLines + 1) {
preg_match("!(.*?\n){".($numLines)."}$!", $data, $match);
fclose($fp);
return
$match[0];
}
}
fclose($fp);
return
$data;
}
?>
mhinks at gmail dot com
17 年前
这是一个用于在文件中以二分法搜索文本行的函数,当文件过大而无法一次性读入内存又需要比线性搜索更快的搜索时,尤其有用。

function binary_search_in_file($filename, $search) {

//打开文件
$fp = fopen($filename, 'r');

//寻找结尾
fseek($fp, 0, SEEK_END);

//获取最大值
$high = ftell($fp);

//设置最小值
$low = 0;

while ($low <= $high) {
$mid = floor(($low + $high) / 2); // C 为你取整

//寻找中点
fseek($fp, $mid);

if($mid != 0){
//读取一行以移动到行尾
$line = fgets($fp);
}

//读取一行以获取数据
$line = fgets($fp);


if ($line == $search) {
fclose($fp);
return $line;
}
else {
if ($search < $line) {
$high = $mid - 1;
}
else {
$low = $mid + 1;
}
}
}

//关闭指针
fclose($fp);

return FALSE;

}
mail at ulf-kosack dot de
18 年前
这里对 ekow 的代码稍作扩展。
如果您想读取多行和多个文件。有时最后的五或十行很有趣。

您只需提交一个带有点阵文件名的数组,以及您想读取的行数(可选)。

<?php
function read_logfiles($files, $lines=5)
{
foreach(
$files as $file_num => $file) {
if (
file_exists ($file) ) {
$handle = fopen($file, "r");
$linecounter = $lines;
$pos = -2;
$t = " ";
$text[$file_num] = "";
while (
$linecounter > 0) {
while (
$t != "\n") {
fseek($handle, $pos, SEEK_END);
$t = fgetc($handle);
$pos --;
}
$t = " ";
$text[$file_num] .= fgets($handle);
$linecounter --;
}
fclose ($handle);
} else {
$text[$file_num] = "文件不存在。";
}
}

return
$text;
?>
To Top