PHP fseek()函数

fseek()函数用于在打开的文件中移动文件指针到指定位置,支持随机文件访问。

语法

int fseek ( resource $handle , int $offset [, int $whence = SEEK_SET ] )

参数说明

参数 描述
handle 文件指针资源,通常由fopen()函数创建
offset 偏移量,表示要移动的字节数
whence 偏移量参考位置,可选值:
  • SEEK_SET - 从文件开头开始(默认)
  • SEEK_CUR - 从当前位置开始
  • SEEK_END - 从文件末尾开始

whence参数详解

常量 描述 示例
SEEK_SET 0 从文件开头计算偏移量 fseek($fp, 100, SEEK_SET) - 移动到第100字节
SEEK_CUR 1 从当前位置计算偏移量 fseek($fp, 50, SEEK_CUR) - 从当前位置向前移动50字节
SEEK_END 2 从文件末尾计算偏移量 fseek($fp, -20, SEEK_END) - 移动到文件末尾前20字节

返回值

  • 成功时返回0
  • 失败时返回-1

示例代码

示例1:基本文件指针操作

<?php
// 创建测试文件
$content = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
file_put_contents('test.txt', $content);

$handle = fopen('test.txt', 'r');
if (!$handle) {
    die('无法打开文件');
}

echo "文件内容: " . htmlspecialchars($content) . "<br><br>";

// 1. 使用SEEK_SET(默认) - 从文件开头
fseek($handle, 5, SEEK_SET);
echo "移动到第5字节: " . htmlspecialchars(fread($handle, 1)) . "<br>"; // 输出: 5

// 2. 使用SEEK_CUR - 从当前位置
fseek($handle, 3, SEEK_CUR);
echo "从当前位置向前移动3字节: " . htmlspecialchars(fread($handle, 1)) . "<br>"; // 输出: 9

// 3. 使用SEEK_END - 从文件末尾
fseek($handle, -5, SEEK_END);
echo "移动到文件末尾前5字节: " . htmlspecialchars(fread($handle, 1)) . "<br>"; // 输出: V

// 4. 负偏移量(只能在SEEK_END中使用,其他情况下会出错)
fseek($handle, -10, SEEK_SET); // 这会失败
echo "负偏移量测试: " . (fseek($handle, -10, SEEK_SET) === -1 ? '失败' : '成功') . "<br>";

fclose($handle);
unlink('test.txt');
?>

示例2:读取文件的特定部分

<?php
// 创建测试文件
$content = <<<EOT
这是一个测试文件,包含多行内容。
第二行:这里有一些数据。
第三行:这是另一行数据。
第四行:文件读取示例。
第五行:最后一行内容。
EOT;

file_put_contents('sample.txt', $content);

function readFileChunk($filename, $start, $length) {
    $handle = fopen($filename, 'r');
    if (!$handle) return false;

    // 移动到指定位置
    if (fseek($handle, $start, SEEK_SET) === -1) {
        fclose($handle);
        return false;
    }

    // 读取指定长度的内容
    $chunk = fread($handle, $length);
    fclose($handle);

    return $chunk;
}

// 使用示例
echo "<h5>读取文件的特定部分:</h5>";

// 读取从第30字节开始的20个字节
$chunk1 = readFileChunk('sample.txt', 30, 20);
echo "从第30字节开始的20字节: " . htmlspecialchars($chunk1) . "<br><br>";

// 读取文件末尾的15个字节
$filesize = filesize('sample.txt');
$chunk2 = readFileChunk('sample.txt', $filesize - 15, 15);
echo "文件末尾的15字节: " . htmlspecialchars($chunk2) . "<br><br>";

// 读取整个文件(分块读取)
$handle = fopen('sample.txt', 'r');
echo "分块读取文件:<br>";
$chunkSize = 20;
$position = 0;
while (!feof($handle)) {
    fseek($handle, $position);
    $chunk = fread($handle, $chunkSize);
    if ($chunk !== false && strlen($chunk) > 0) {
        echo "位置 {$position}: " . htmlspecialchars($chunk) . "<br>";
        $position += $chunkSize;
    }
}
fclose($handle);

unlink('sample.txt');
?>

示例3:二进制文件处理

<?php
// 创建二进制文件
$binaryData = '';
for ($i = 0; $i < 256; $i++) {
    $binaryData .= chr($i); // 0x00 到 0xFF
}
file_put_contents('binary.dat', $binaryData);

// 读取二进制文件的特定位置
function readBinaryAtPosition($filename, $position, $length = 1) {
    $handle = fopen($filename, 'rb'); // 二进制模式
    if (!$handle) return false;

    if (fseek($handle, $position, SEEK_SET) === -1) {
        fclose($handle);
        return false;
    }

    $data = fread($handle, $length);
    fclose($handle);

    return $data;
}

// 使用示例
echo "<h5>二进制文件操作:</h5>";

// 读取特定位置的字节
echo "位置 65 的字节: ";
$byte = readBinaryAtPosition('binary.dat', 65);
echo "ASCII: " . ord($byte) . " (" . htmlspecialchars($byte) . ")<br>";

// 读取文件头(前4个字节)
echo "文件头 (前4字节): ";
$header = readBinaryAtPosition('binary.dat', 0, 4);
for ($i = 0; $i < strlen($header); $i++) {
    echo sprintf('%02X ', ord($header[$i]));
}
echo "<br>";

// 读取文件尾(最后4个字节)
echo "文件尾 (最后4字节): ";
$filesize = filesize('binary.dat');
$footer = readBinaryAtPosition('binary.dat', $filesize - 4, 4);
for ($i = 0; $i < strlen($footer); $i++) {
    echo sprintf('%02X ', ord($footer[$i]));
}
echo "<br>";

// 搜索特定字节值
function findByteInBinaryFile($filename, $searchByte) {
    $handle = fopen($filename, 'rb');
    if (!$handle) return false;

    $position = 0;
    while (!feof($handle)) {
        $byte = fread($handle, 1);
        if ($byte === false) break;

        if (ord($byte) === $searchByte) {
            fclose($handle);
            return $position;
        }
        $position++;
    }

    fclose($handle);
    return false;
}

$searchFor = 0x41; // 搜索 'A' 的ASCII值
$foundAt = findByteInBinaryFile('binary.dat', $searchFor);
if ($foundAt !== false) {
    echo "找到字节 0x" . dechex($searchFor) . " 在位置: {$foundAt}<br>";
}

unlink('binary.dat');
?>

示例4:随机文件访问类

<?php
class RandomAccessFile {
    private $handle;
    private $filename;
    private $mode;
    private $position = 0;

    public function __construct($filename, $mode = 'r') {
        $this->filename = $filename;
        $this->mode = $mode;
        $this->handle = fopen($filename, $mode);

        if (!$this->handle) {
            throw new Exception("无法打开文件: {$filename}");
        }
    }

    /**
     * 移动到指定位置
     */
    public function seek($offset, $whence = SEEK_SET) {
        $result = fseek($this->handle, $offset, $whence);

        if ($result === 0) {
            $this->position = ftell($this->handle);
            return true;
        }

        return false;
    }

    /**
     * 读取指定长度的数据
     */
    public function read($length) {
        $data = fread($this->handle, $length);
        if ($data !== false) {
            $this->position += strlen($data);
        }
        return $data;
    }

    /**
     * 写入数据
     */
    public function write($data) {
        $bytes = fwrite($this->handle, $data);
        if ($bytes !== false) {
            $this->position += $bytes;
        }
        return $bytes;
    }

    /**
     * 获取当前位置
     */
    public function getPosition() {
        return ftell($this->handle);
    }

    /**
     * 获取文件大小
     */
    public function getSize() {
        $current = $this->getPosition();
        fseek($this->handle, 0, SEEK_END);
        $size = ftell($this->handle);
        $this->seek($current);
        return $size;
    }

    /**
     * 读取一行
     */
    public function readLine() {
        $line = fgets($this->handle);
        if ($line !== false) {
            $this->position += strlen($line);
        }
        return $line;
    }

    /**
     * 关闭文件
     */
    public function close() {
        if ($this->handle) {
            fclose($this->handle);
            $this->handle = null;
        }
    }

    /**
     * 跳转到文件开头
     */
    public function rewind() {
        return $this->seek(0, SEEK_SET);
    }

    /**
     * 跳转到文件末尾
     */
    public function end() {
        return $this->seek(0, SEEK_END);
    }

    public function __destruct() {
        $this->close();
    }
}

// 使用示例
try {
    // 创建测试文件
    $testData = "Line 1: Hello World\nLine 2: PHP fseek Example\nLine 3: Random Access\nLine 4: End of File";
    file_put_contents('testfile.txt', $testData);

    $file = new RandomAccessFile('testfile.txt', 'r+');

    echo "<h5>随机文件访问示例:</h5>";

    // 读取第一行
    echo "第一行: " . htmlspecialchars($file->readLine()) . "<br>";

    // 移动到第30字节
    $file->seek(30);
    echo "第30字节开始读取: " . htmlspecialchars($file->read(10)) . "<br>";

    // 获取当前位置
    echo "当前位置: " . $file->getPosition() . "<br>";

    // 获取文件大小
    echo "文件大小: " . $file->getSize() . " 字节<br>";

    // 移动到文件末尾
    $file->end();
    echo "移动到文件末尾,位置: " . $file->getPosition() . "<br>";

    // 从末尾向前移动并读取
    $file->seek(-15, SEEK_END);
    echo "文件末尾前15字节: " . htmlspecialchars($file->read(15)) . "<br>";

    // 跳回文件开头
    $file->rewind();
    echo "重新回到文件开头,读取: " . htmlspecialchars($file->read(5)) . "<br>";

    $file->close();

    unlink('testfile.txt');

} catch (Exception $e) {
    echo "错误: " . $e->getMessage();
}
?>

注意事项

重要提示:
  • 二进制模式:在Windows系统上处理二进制文件时,需要使用'rb''wb+'等二进制模式
  • 偏移量限制:某些文件系统(如32位系统)可能不支持大于2GB的文件偏移量
  • 负偏移量:负偏移量只能与SEEK_END一起使用,与SEEK_SET使用会导致错误
  • 网络流:对于网络流(HTTP、FTP等),fseek()可能不被支持或表现不一致
  • 性能考虑:频繁的fseek()调用可能影响性能,特别是在远程文件或慢速设备上
  • 文件锁定:在多进程环境中使用fseek()时,考虑使用flock()进行文件锁定
  • 错误处理:始终检查fseek()的返回值,失败时返回-1
  • 与ftell配合:fseek()常与ftell()配合使用,获取当前位置
  • UTF-8编码:处理UTF-8文本时,注意多字节字符可能占用多个字节

fseek() vs rewind()

特性 fseek() rewind()
功能 移动到文件任意位置 移动到文件开头
参数 需要偏移量和参考位置 不需要参数
灵活性 高,可精确定位 低,只能回到开头
错误处理 返回0成功,-1失败 成功返回true,失败返回false
使用场景 随机文件访问 重新读取文件

相关函数

  • ftell() - 返回文件指针读/写的位置
  • rewind() - 倒回文件指针的位置
  • fopen() - 打开文件或URL
  • fread() - 读取文件(二进制安全)
  • fwrite() - 写入文件
  • feof() - 测试文件指针是否到达文件末尾
  • stat() - 获取文件信息

典型应用场景

二进制文件解析

解析图片、音频、视频等二进制文件的特定部分。

日志文件分析

从大型日志文件的特定位置开始读取或搜索。

数据库索引文件

在索引文件中快速定位和读取数据记录。

文件编辑器

实现文本编辑器的跳转到行、跳转到位置功能。