mclee 's Blog
Love Leanote!
Toggle navigation
mclee 's Blog
主页
PHP
归档
标签
PHP 处理文件
无
2018-02-08 22:51:56
307
0
0
mclee
# 简介 PHP 的文件 I/O 接口与 C 中类似,不过没有那么复杂。标识要读写的文件时,所使用的基本单位是文件句柄。这个句柄标识与一个特定文件的连接,可以用它来完成文件相关操作。这个句柄通常由 fopen() 返回传入其他 I/O 函数,如 fgets()、fputs() 和 fclose()。 # 打开或创建一个文件 ## fopen resource fopen ( string $filename , string $mode [, bool $use_include_path = false [, resource $context ]] ) **fopen() 将 filename 指定的名字资源绑定到一个流上。** 如果 filename 是 "scheme://..." 的格式,则被当成一个 URL,PHP 将搜索协议处理器(也被称为封装协议)来处理此模式。如果该协议尚未注册封装协议,PHP 将发出一条消息来帮助检查脚本中潜在的问题并将 filename 当成一个普通的文件名继续执行下去。 如果 PHP 认为 filename 指定的是一个本地文件,将尝试在该文件上打开一个流。该文件必须是 PHP 可以访问的,因此需要确认文件访问权限允许该访问。如果激活了安全模式或者 open_basedir 则会应用进一步的限制。 如果 PHP 认为 filename 指定的是一个已注册的协议,而该协议被注册为一个网络 URL,PHP 将检查并确认 allow_url_fopen 已被激活。如果关闭了,PHP 将发出一个警告,而 fopen 的调用则失败。 模式 | 操作 | 文件指针位置 | 是否清除原有内容 | 当文件不存在 ---- | ---- | ------------ | ---------------- | ------------ rb | 读 | 开头 | 否 | 报错,返回 false rb+ | 读写 | 开头 | 否 | 报错,返回 false wb | 写 | 开头 | 是 | 尝试创建 wb+ | 读写 | 开头 | 是 | 尝试创建 ab | 写 | 末尾 | 否 | 尝试创建 ab+ | 读写 | 末尾 | 否 | 尝试创建 xb | 写 | 开头 | 否 | 尝试创建;若文件存在,报错,返回 false xb+ | 读写 | 开头 | 否 | 尝试创建;若文件存在,报错,返回 false cb | 写 | 开头 | 否 | 尝试创建 cb+ | 读写 | 开头 | 否 | 尝试创建  ### 打开本地文件 ``` <?php // 打开一个本地文件,将返回的资源句柄付给 $handle,$php_errormsg 是 php 预定义变量,存储着上一个错误信息 $handle = fopen('./test.txt', 'rb') or die($php_errormsg); ``` ### 打开远程文件 希望打开一个可以通过 HTTP 或 FTP 访问的文件时可直接传入文件的 RUL。 $fh = fopen('http://example.com/robots.txt', 'r') or die($php_errormsg); 可以通过添加 $context 参数添加上下文,如改变请求方式,添加请求头。 向 fopen() 传入一个以 ftp:// 开头的 URL 时,会返回指定文件的一个指针。可以通过 FTP 打开文件来完成读或写,**但不能同时读写。** ``` <?php // 需要把认证信息嵌入在 URL 中,先读取,在写入。 $fh = fopen('ftp://username:password@ftp.example.com/robots.txt', 'rb'); $fh = fopen('ftp://username:password@ftp.example.com/robots.txt', 'wb'); ``` 用 fopen() 打开远程文件,这个特性默认是启用的,不过可以在 php.ini 或 web 服务器配置文件中将 allow_url_fopen 设置为 off 来禁用。如果无法用 fopen() 打开远程文件,请检查相关配置项。 > http://php.net/manual/zh/function.fopen.php ## tempfile 创建临时文件 resource tmpfile ( void ) 以读写(w+)模式建立一个具有唯一文件名的临时文件,返回一个文件句柄。 文件会在关闭后(用 fclose())自动被删除,或当脚本结束后。 ``` <?php // 这个临时文件会在关闭后(用 fclose())自动被删除,或当脚本结束后。 $temp_fh = tmpfile(); // fwrite 的别名函数 fputs($temp_fh, 'data'); ``` ## touch 的别样用法 bool touch ( string $filename [, int $time = time() [, int $atime ]] ) 设定文件的修改时间(mtime)和访问时间(atime) 尝试将由 filename 给出的文件的访问和修改时间设定为给出的 time。 注意访问时间总是会被修改的,不论有几个参数。 **如果文件不存在,则会被创建。** linux 下文件通常有三个时间属性,atime(上一次访问时间),mtime(上一次内容修改时间),ctime(上一次状态变更时间)。ctime 在文件的所有者、权限等元数据信息变更时会改变,要注意的是当文件内容变化时 ctime 也会变成 mtime 的值。 linux 下没有文件创建时间,windows 有。windows 下的文件的时间属性为:上一次访问时间 + 上一次修改时间 + 创建时间。 # 读取内容 ## 读取标准输入 ``` <?php $fh = fopen('php://input', 'rb') or die ($php_errormsg); while ($s = fgets($fh)) { echo $s; } ``` 另一种更推荐的写法: ``` <?php while ($s = fgets(STDIN)) { echo $s; } ``` 还可以通过 PHP 预定义变量获取,在命令行下传给脚本的参数。不过这只能在调用时传参,而上面的方式可在运行时从标准输入获取输入。 * \$argc — 传递给脚本的参数数目 * $argv — 传递给脚本的参数数组 从标准输入读取数据在 web 环境中用处并不大,因为大多数信息不是通过标准输入传递的。HTTP POST 和文件上传请求的体由 PHP 解析,并放到特殊变量中(超全局数组)。**但非文件上传的 POST 请求体可以用 php://stdin 读取。** ## 一次性获取文件的全部内容 ### file_get_contents 读到字符串 string file_get_contents ( string $filename [, bool $use_include_path = false [, resource $context [, int $offset = -1 [, int $maxlen ]]]] ) file_get_contents() 函数是用来将文件的内容读入到一个字符串中的首选方法。 如果只是想打印一个文件的全部内容,与文件读入一个字符串再打印的做法相比,还有更容易更高效的做法。PHP 为此提供了 2 个函数。fpassthru() 和 readfile()。 ### fpassthru() 从指针位置开始输出 int fpassthru ( resource $handle ) fpassthru(\$fh),这会打印文件句柄 $fh 其余的全部内容,然后将它关闭。将给定的文件指针从当前的位置读取到 EOF 并把结果写到输出缓冲区。如果已经向文件写入数据,就必须调用 rewind() 来将文件指针指向文件头。 ### readfile() 直接输出 如果既不修改文件也不在特定位置检索,只想将文件的内容写到输出缓冲区,应该使用 readfile(),这样可以省去 fopen() 调用。 int readfile ( string $filename [, bool $use_include_path = FALSE [, resource $context ]] ) readfile() 读取文件并写入到输出缓冲。可用来输出图片: ``` <?php $image_directory = '/usr/loacal/images'; header('Content-Type: image/jpg'); header('Content-Length: ' . filesize($image_directory . '/mclee.jpg')); readfile($image_directory . '/mclee.jpg'); ``` 这个 $image_directory 应当在 web 服务器文档根目录之外,这样上面的脚本才有意义,否则用户可以直接访问图片。 ### file() 读到数组 array file ( string $filename [, int $flags = 0 [, resource $context ]] ) 可选参数 flags 可以是以下一个或多个常量: * FILE_USE_INCLUDE_PATH 在 include_path 中查找文件。 * FILE_IGNORE_NEW_LINES 在数组每个元素的末尾不要添加换行符 * FILE_SKIP_EMPTY_LINES 跳过空行 ## fgets() 读取文件中的一行 string fgets ( resource $handle [, int $length ] ) ## fread() 读取指定长度字符 string fread ( resource $handle , int $length )  > http://php.net/manual/zh/function.fread.php ## fgetc() 从文件指针中读取字符 string fgetc ( resource $handle ) ## fgetcsv() 处理特定分隔符文本 csv 格式不等于逗号分隔,可以是其他的字符。 array fgetcsv ( resource $handle [, int $length = 0 [, string $delimiter = ',' [, string $enclosure = '"' [, string $escape = '\\' ]]]] ) 使用 getcsv() 读入各行,然后根据它们的定界符划分字符: ``` <?php // 设置字段分界符(只允许一个字符)。 $delim = '|'; $fh = fopen('books.txt', 'rb') or die($php_errormsg); while (!feof($fh)) { $fields = fgetcsv($fh, filesize('books.txt'), $delim); print_r($fields); } fclose($fh) or die($php_errormsg); ``` > http://php.net/manual/zh/function.fgetcsv.php ## parse_ini_file() 读取 ini 配置文件 ``` <?php $ini_path = php_ini_loaded_file(); // 第二个参数默认为 false 不产生 section 键,传 true 使对应 section 下的配置归到数组中名为对应 section 的键下 $ini = parse_ini_file($ini_path, true); print_r($ini); ```  > http://php.net/manual/zh/function.parse-ini-file.php # 写入内容 ## file_put_contents() 直接输出到文件 int file_put_contents ( string $filename , mixed $data [, int $flags = 0 [, resource $context ]] ) 和依次调用 fopen(),fwrite() 以及 fclose() 功能一样。  > http://php.net/manual/zh/function.file-put-contents.php ## fwrite() / fputs() 写入字符串到资源句柄 int fwrite ( resource $handle , string $string [, int $length ] ) 常用来输出字符串到资源句柄,不过需要先有一个资源句柄,而 file_put_contents() 就不用所以更加方便。fputs() 是 fwrite 的别名。 ## fflush() 立即刷新到文件 bool fflush ( resource $handle ) 本函数强制将所有缓冲的输出写入 handle 文件句柄所指向的资源。 成功时返回 TRUE, 或者在失败时返回 FALSE。 文件指针必须是有效的,必须指向由 fopen() 或 fsockopen() 成功打开的文件(并还未由 fclose() 关闭)。 ## 写标准输出 使用 echo 和 print。根据手册描述这两个都不是函数,而是语言结构,但都可以加 () 调用。print 可以用在表达式中(如三元表达式),但 echo 不能,且 echo 后加 () 使用时,不能传多个参数,没括号的情况下逗号分隔多个待输出项。 >http://php.net/manual/zh/function.echo.php http://php.net/manual/zh/function.print.php ## popen() 打开指向一个进程的管道 resource popen ( string $command , string $mode ) mode 参考 fopen() 的 mode 参数。对管道的读写可使用之前介绍的那些文件读写函数。 # unlink() 删除文件 bool unlink ( string $filename [, resource $context ] ) unlink() 函数只能删除 PHP 进程用户能够删除的文件。若使用 unlin() 删除文件时遇到问题,要检查文件的权限以及运行 PHP 的方式。 # copy() 复制文件 bool copy ( string $source , string $dest [, resource $context ] ) # rename() 移动文件 bool rename ( string $oldname , string $newname [, resource $context ] ) # flock() 锁定文件 bool flock ( resource $handle , int $operation [, int &$wouldblock ] ) 锁机制之所以存在是因为并发问题导致的资源竞争,为了确保操作的有效性和完整性,可以通过锁机制将并发状态转换成串行状态。 operation 可以是以下值之一: * LOCK_SH 共享锁(读锁) * LOCK_EX 排他锁(写锁) * LOCK_UN 释放锁(无论共享或排他) 如果不希望 flock() 在锁定时堵塞,则是 LOCK_NB。可用 | 异或连接标识符。可能有的组合 LOCK_EX | LOCK_NB,LOCK_SH | LOCK_NB。 * 共享锁(读锁): 一个资源可加多个共享锁,一个持有共享锁的资源在被释放掉所有的共享锁之前不可以加排他锁,否则会上锁程序默认会阻塞,若不想阻塞可加 LOCK_NB 标识。其他进程可加 * 排他锁(写锁): 一个资源只能加一个排他锁,一个持有写锁的资源在释放掉该锁前,无法上任何其他的锁。 对有可能被并发读写的文件,应先加锁后执行读写操作。这是一种为了解决问题做的协商,实际编写程序是并不要求一定要加锁再进行操作,没加锁也可以直接执行读写,但这可能会产生读脏数据等问题。加锁只是为了限制接下来程序对锁定文件进行的操作,从而获得我们想要的结果,所以不加锁只是没了限制。充分利用这种限制才是利用好锁的关键。 * 读的时候: 如果不想出现dirty数据,那么最好使用lock_sh共享锁。可以考虑以下三种情况: 1. 如果读的时候没有加共享锁,那么其他程序要写的话(不管这个写是加锁还是不加锁)都会立即写成功。如果正好读了一半,然后被其他程序给写了,那么读的后一半就有可能跟前一半对不上(前一半是修改前的,后一半是修改后的) 2. 如果读的时候加上了共享锁,这个时候,其他程序开始写,这个写程序没有使用锁,那么写程序会直接修改这个文件,也会导致前面一样的问题。 3. 最理想的情况是,读的时候加锁(lock_sh),写的时候也进行加锁(lock_ex),这样写程序会等着读程序完成之后才进行操作,而不会出现贸然操作的情况 * 写的时候: 如果多个写程序不加锁同时对文件进行操作,那么最后的数据有可能一部分是a程序写的,一部分是b程序写的。 如果写的时候加锁了,这个时候有其他的程序来读,那么他会读到什么东西呢? 1. 如果读程序没有申请共享锁,那么他会读到dirty的数据。比如写程序要写a,b,c三部分,写完a,这时候读读到的是a,继续写b,这时候读读到的是ab,然后写c,这时候读到的是abc. 2. 如果读程序在之前申请了共享锁,那么读程序会等写程序将abc写完并释放锁之后才进行读。 > 一篇博客 https://www.cnblogs.com/chenwenbiao/archive/2011/08/01/2123905.html # 综合运用-读写压缩文件 使用 compress.zlib 或 compress.bzip2 流包装器。使用 stream_get_wrappers() 获取已注册的数据流过滤器列表。 ## 读写本地压缩文件 要从一个 gzip 压缩文件读取数据。 ``` <?php $fh = fopen('compress.zlib://mclee.txt.gz', 'rb') or die($php_errormsg); print fgets($fh); ``` compress.zlib 流包装器允许访问用 gzip 算法压缩的文件。compress.bzip2 刘包装器允许访问用 bzip2 算法压缩的文件。这两个流包装器都允许读、写和追加内容。要启用这两个压缩流,要在构建 PHP 时提供 --with-zlib 和 --with-bz2。 ## 读写远程压缩文件 除了流包装器允许访问压缩的本地文件,还有一些流过滤器可以动态的压缩或解压缩任意的流。zlib.deflate 和 zlib.inflate 过滤去会根据 zlib "deflate" 算法压缩和解压缩数据。bzip2.compress 和 bzip2.uncompress 过滤器则根据 bzip2 算法压缩和解压缩数据。 使用 stream_get_filters() 获取已注册的数据流过滤器列表 ``` <?php $fh = fopen('http://www.example.com/something-copressed.bz2', 'rb') or die($php_errormsg); stream_filter_append($fh, 'bzip2.uncompress'); // 打印通过流过滤器解压缩以后的数据 print fgets(($fh); fclose($fh); ``` # SPL 文件处理相关  > http://php.net/manual/zh/book.spl.php spl 文件处理相关类,为文件处理提供了面向对象接口。如 SqlFIleObject 类就囊括了之前介绍的大多数方法。这里只简单介绍一点。SqlFIleObject 类继承自 SqlFileInfo 类,提供了处理文件,获取文件信息的接口。 public SplFileObject::__construct ( string $filename [, string $open_mode = "r" [, bool $use_include_path = FALSE [, resource $context ]]] ) ``` <?php $file = new SplFileObject('mclee.txt', 'wb+'); $file->fwrite("mclee"); // 上一步只是写到了内存里,并没有实际写到硬盘上,故一定要刷新。 $file->fflush(); // 由于写过了,文件指针位置已经到了末尾,要复位才能从头获取内容 $file->rewind(); print $file->fgets(); ``` 该类并没有提供删除文件的接口,若想删除,可参考手册上的例子: ``` <?php // (PHP >= 5.3) If filename is a directory, a LogicException will be thrown: "Cannot use SplFileObject with directories" $file = new SplFileObject('example.txt'); // 解除引用 $file = null; // 调用 unlink 删除文件,没有上一步直接执行这一步会失败 unlink('example.txt'); /* Note that this class has a private (and thus, not documented) property that holds the file pointer. Combine this with the fact that there is no method to close the file handle, and you get into situations where you are not able to delete the file with unlink(), etc., because an SplFileObject still has a handle open. */ ``` 由于实现了迭代器接口可方便的直接对实例进行迭代: ``` <?php $file = new SplFileObject(__FILE__); foreach ($file as $line_num => $line) { echo "Line $line_num is $line"; } ``` > http://php.net/manual/zh/class.splfileobject.php
上一篇:
PHP 处理目录及文件信息
下一篇:
PHP 文件包含
0
赞
307 人读过
新浪微博
微信
腾讯微博
QQ空间
人人网
提交评论
立即登录
, 发表评论.
没有帐号?
立即注册
0
条评论
More...
文档导航
没有帐号? 立即注册