php 函数中 ftell 的输出

the output of ftell in php function

这是我的代码:

<?php
    $url="http://www.sina.com.cn";
    $handle = @fopen($url, "r");
    $len=get_headers($url,true);
    print_r($len);
    echo $len['Content-Length']."\n";
    if ($handle) {
        while (($buffer = fgets($handle,1024)) !== false) {
            echo ftell($handle)."\n";
            fseek($handle,200000,SEEK_CUR);
            echo ftell($handle)."\n";
        }
        if (!feof($handle)) {
            echo "Error: unexpected fgets() fail\n";
        }
        fclose($handle);
    }
?>

结果如下:

    Array
(
    [0] => HTTP/1.1 200 OK
    [Content-Type] => text/html
    [Vary] => Accept-Encoding
    [X-Powered-By] => shci_v1.03
    [Server] => nginx
    [Date] => Thu, 24 Dec 2015 04:03:39 GMT
    [Last-Modified] => Thu, 24 Dec 2015 04:01:28 GMT
    [Expires] => Thu, 24 Dec 2015 04:04:39 GMT
    [Cache-Control] => max-age=60
    [Age] => 32
    [Content-Length] => 518264
    [X-Cache] => HIT from xidan33-99.sina.com.cn
    [Connection] => close
)
518264
16
200016
200058
400058
400065
518264

Content-Length可能和我的不一样--518264,执行代码的时候会动态变化,不讨论。 为什么结果不是下面的?

518264
1024
201024
202048
402048
403072

请解释文件指针对 fgets、ftell 和 fseek 函数的作用。

fgets的length参数表示最大长度。 PHP documentation 状态:

Reading ends when length - 1 bytes have been read, or a newline (which is included in the return value), or an EOF (whichever comes first). If no length is specified, it will keep reading from the stream until it reaches the end of the line.

在你的例子中,第一行包含 <!DOCTYPE html>,它解释了 ftell 给出的 16 的结果。

三个函数可用于设置和确定给定文件的文件指针位置。

fgets()

从文件指针中获取一行。它将假定 1024 作为行长度。如果文件中的大部分行都大于 8KB,则脚本指定最大行长度会更节省资源。

Returns 从 handle 指向的文件读取的最大长度为 - 1 字节的字符串。如果文件指针中没有更多数据可读,则返回 FALSE

ftell()

内置函数:pos = ftell (fid)

Return 文件指针的位置为从文件描述符指定的文件开头算起的字符数fid

fseek()

内置函数:fseek (fid, offset) 内置函数:fseek (fid, offset, origin)

将文件指针设置为文件 fid 中的位置偏移量。

指针位于原点的偏移字符处,原点可以是预定义变量之一SEEK_CUR (current position), SEEK_SET (beginning),SEEK_END (end of file)或字符串"cof", "bof" or "eof".如果省略原点,SEEK_SET 假设。偏移量可以是正数、负数或零,但并非所有原点和偏移量的组合都可以实现。

fseek returns 0 表示成功,-1 表示错误。

根据 fgets() 的 PHP 文档,

Reading ends when length - 1 bytes have been read, or a newline (which is included in the return value), or an EOF (whichever comes first).

这里的length是你在调用fgets()时使用的第二个参数,即1024。因此,当发生以下任一情况时,您对 fgets() 的调用将结束阅读:

  1. 它从同一行读取了 1023 个字节。
  2. 已到达当前行的末尾。
  3. 已到达文件末尾。

因此,在您的情况下,当 fgets() 读取第一行时,它在读取 16 字节后到达第一行的末尾,这就是位置旁边的 ftell() 调用时的文件指针。 ftell()returns当前文件指针在文件中的位置

当您在下一行再次调用 fgets()(由 while 循环迭代)时,您在文件中的起始位置现在是 16(尤其不是 1024),您最多可以读取 (16 + 1024) 1040 字节(不是 2048)。同样,如果您的下一行只有 42 字节,则此 fgets() 将在 58 字节处结束读取,这将是ftell() 再次调用时的文件指针。

然后您将再次从 58 字节开始下一个 fgets(),最多读取 (58 + 1024 =) 1082 字节。它会继续这样。

fseek()
的效果 fseek() 用于将文件指针移动到文件中由 $offset(第二个参数)设置的特定位置。根据 fseek() 的 PHP 文档,第三个参数值可以是 -

SEEK_SET - 设置位置等于偏移字节。
SEEK_CUR - 将位置设置为当前位置加上偏移量。
SEEK_END - 将位置设置为文件末尾加上偏移量。

因此,通过 fseek($handle,200000,SEEK_CUR); 您将文件指针设置为 200000 + 当前位置。比如在16的时候会移动到200016。

使用PHP函数stream_get_meta_data()查明您打开的流是否可搜索:

$url="http://www.sina.com.cn";
$handle = @fopen($url, "r");

$meta_data = stream_get_meta_data($handle);
var_dump($meta_data['seekable']);

// It prints `bool(false)`

流不可搜索。这意味着函数 fseek(), ftell() and rewind() 具有意外(并且可能不一致)的行为。