Menu

PHP file_get_contents():把文件读取为字符串

file_get_contents('notes.txt') 读取整个文件并以一个字符串返回,读取失败时返回 false。与它配套的 file_put_contents() 把字符串写入文件,file() 则以数组形式返回文件的各行。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

file_get_contents('notes.txt') 读取整个文件,并以一个字符串返回。文件无法读取时返回 false。写入文件用 file_put_contents('notes.txt', $text),它会创建文件,或者替换文件原有的内容。

输出:

Buy milk
Call Ada
Write docs
29 bytes
3 lines

这段代码先写入 notes.txt,再把它读回来,这样就有文件可以操作了。像 'notes.txt' 这样的路径是相对于当前工作目录的;在 Web 应用中,要用 __DIR__ . '/notes.txt' 基于脚本自身所在的文件夹构建路径。

用 file_put_contents() 写入和追加

file_put_contents() 返回写入的字节数,失败时返回 false。默认情况下它会覆盖文件。FILE_APPEND 标志则把内容加到文件末尾,简单的日志就是这样实现的;LOCK_EX 可以防止两个请求同时写入:

输出:

Wrote 8 bytes
started
step 1 done
step 2 done

replaced

abc
5 bytes

要存储一个数组并把它作为数组读回来,用 json_encode() 写成 JSON,再用 json_decode() 读取。

用 file() 逐行读取文件

file() 把文件读取到数组中,每个元素是一行。除非传入 FILE_IGNORE_NEW_LINES,否则每行末尾都保留换行符;FILE_SKIP_EMPTY_LINES 会丢弃空行(它只有和 FILE_IGNORE_NEW_LINES 一起使用才有效):

输出:

string(9) "Buy milk
"
1. Buy milk
2. Call Ada
3. Write docs
3 tasks

file() 和 file_get_contents() 都会把整个文件加载到内存中。对于配置文件和几 MB 大小的日志,这没有问题。对于几百 MB 的文件,应改用 fopen() 和 fgets() 每次读取一行。

读取前检查文件是否存在

文件不存在时,file_get_contents() 返回 false,并且 PHP 会输出一条警告:

Warning: file_get_contents(missing.txt): Failed to open stream: No such file or directory in /home/index.php on line 2

先用 is_file()(存在且不是目录的文件)或 file_exists()(文件或目录)检查,并且始终用 === false 比较结果,因为空文件会返回 "",它同样是假值:

输出:

empty.txt   exists file 
missing.txt missing 
.           exists directory 
Cannot read missing.txt, using defaults
bool(false)
bool(true)
0 bytes

不要用 @file_get_contents() 隐藏警告。先检查,或者处理 false,这样其他地方出问题时错误仍然可见。

读取文件的一部分

第四个和第五个参数分别是开始读取的字节偏移量和要读取的字节数。负数偏移量从文件末尾开始计算。用命名参数可以跳过中间的两个参数:

输出:

HEADER
line 2
last line

偏移量按字节计算,而不是按字符。在 UTF-8 中,一个中文字符占 3 个字节,所以偏移量落在字符中间就会把它切成两半。要按字符处理,先读取整个字符串,再用 mb_substr()。

在网页上显示文本文件

文件内容是文本,而输出到 HTML 中的文本必须转义,否则文件里的 < 会变成标签。htmlspecialchars() 负责转义,nl2br() 把换行符转换成 <br> 标签。运行它,然后查看“页面”标签页:

去掉 htmlspecialchars() 再运行一次:页面现在会在“v1.2: Fixed”处结束,因为浏览器把 <title> 当成了真正的标签,并把它后面的所有内容都当作页面标题吞掉了。htmlspecialchars() 页面解释了这对用户可以写入的任何内容为什么重要。

用 file_get_contents() 读取 URL

启用 allow_url_fopen 时(php.ini 中的默认设置),file_get_contents() 还能读取 URL,并返回响应正文。流上下文可以添加请求方法、请求头、请求体和超时设置:

<?php
$html = file_get_contents('https://example.com/');

$context = stream_context_create([
    'http' => [
        'method'  => 'POST',
        'header'  => "Content-Type: application/json\r\n",
        'content' => json_encode(['name' => 'Ada']),
        'timeout' => 5,
        'ignore_errors' => true,   // return the body of 4xx/5xx responses too
    ],
]);

$response = file_get_contents('https://api.example.com/users', false, $context);
if ($response === false) {
    // DNS failure, timeout, or a refused connection
}

需要重试、有次数限制的重定向和清晰的错误码时,cURL 或 Guzzle 这样的 HTTP 客户端库是更好的选择。

file_get_contents、file() 与 fopen() 的区别

你想要使用内存占用
把整个文件读成一个字符串file_get_contents()整个文件
把各行读成数组file($path, FILE_IGNORE_NEW_LINES)整个文件
一次调用写入或替换文件file_put_contents()写入的数据
向日志追加一行file_put_contents($path, $line, FILE_APPEND | LOCK_EX)一行
逐行读取大文件,或读取 CSV 行fopen() 配合 fgets() 或 fgetcsv()一行
对同一个已打开的文件读写fopen() 配合 r+、a+ 或 c+读取的内容

这些一次调用就完成的函数会替你打开、读取或写入并关闭文件,所以默认用它们就对了。文件太大、无法放进内存,或者需要在多次操作之间保持文件打开时,再用 fopen()。

常见问题

PHP 中怎么把文件读取为字符串?

调用 file_get_contents('path/to/file.txt')。它以字符串返回整个文件,文件无法读取时返回 false,所以使用结果之前要先用 === false 比较。

PHP 中怎么向文件追加内容?

传入 FILE_APPEND 标志:file_put_contents('log.txt', "new line\n", FILE_APPEND);。不加这个标志,file_put_contents() 会替换整个文件。可能有多个请求同时写入时,再加上 LOCK_EX(FILE_APPEND | LOCK_EX)。

PHP 中怎么逐行读取文件?

小文件用 file('data.txt', FILE_IGNORE_NEW_LINES),它返回一个数组,每个元素是一行。大文件用 fopen() 打开,再在 while 循环里用 fgets() 每次读一行,这样内存中只保留一行。

为什么 file_get_contents 返回 false?

文件不存在、相对路径所相对的目录和你以为的不一样,或者 PHP 没有读取权限。PHP 还会输出类似 Failed to open stream: No such file or directory 的警告。用 is_file() 和 is_readable() 检查,并基于 __DIR__ 构建路径。

file_get_contents 可以读取 URL 吗?

可以,前提是 php.ini 中启用了 allow_url_fopen:file_get_contents('https://example.com/') 会返回响应正文。需要请求头、POST 数据或超时设置时,传入用 stream_context_create() 创建的上下文;更复杂的需求用 cURL,它的错误处理更完善。

Coddy 编程语言插图

和 Coddy 一起学编程

开始