总算来到我们最关心的部分了,也就是 f 相关函数的操作。基本上大部分的文件操作都是以今天学习的这些内容为基础的,话不多说,我们就一个一个的来学习学习吧。

文件读取

文件的读取其实非常简单,fopen() 打开句柄,fread() 读取内容,fclose() 关闭句柄,一套流程下来操作就完成了。

$f = fopen('./test.txt', 'r+');

while (!feof($f)) {
$contents = fread($f, 4);
echo $contents, PHP_EOL;
}
// Rain
// is
// fall
// ing
// all
// arou
// nd, // It f
// alls
// on
// ……
// ……

fopen() 函数的第二个参数是我们可以操作的权限。这个大家应该不会陌生,w 就是可写,r 就是可读,r+ 就是读写方式打开并将文件指针指向文件头,a 是追加写入。

模式 说明
'r' 只读方式打开,将文件指针指向文件头。
'r+' 读写方式打开,将文件指针指向文件头。
'w' 写入方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
'w+' 读写方式打开,将文件指针指向文件头并将文件大小截为零。如果文件不存在则尝试创建之。
'a' 写入方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
'a+' 读写方式打开,将文件指针指向文件末尾。如果文件不存在则尝试创建之。
'x' 创建并以写入方式打开,将文件指针指向文件头。如果文件已存在,则 fopen() 调用失败并返回 FALSE,并生成一条 E_WARNING 级别的错误信息。如果文件不存在则尝试创建之。这和给 底层的 open(2) 系统调用指定 O_EXCL
'x+' 创建并以读写方式打开,其他的行为和 'x' 一样。
'c' 只打开文件进行写入。如果文件不存在,则创建该文件。如果它存在,它既不会被截断(与“w”相反),也不会导致对该函数的调用失败(与“x”一样)
'c+' 打开文件进行读写;否则它的行为与“c”相同。

fread() 函数的第二个参数是每次要读取的字节数,可以看到在测试代码中我们是以 4 个字节为单位进行读取的,所以文件内容都是按 4 个字节分开的一行一行的输出的。feof() 用于判断当前文件的游标指针是否已经移动到末尾了。

游标操作

既然说到游标,那么我们就来看看游标相关的操作。

while (!feof($f)) {
$contents = fread($f, 1024);
echo $contents, PHP_EOL;
}
// rewind($f);
while (!feof($f)) {
$contents = fread($f, 1024);
echo $contents, PHP_EOL;
}
// Rain is falling all around,
// It falls on field and tree,
// It rains on the umbrella here,
// And on the ships at sea.

当使用最上方的代码读取过一遍内容后,游标就已经到底了,这时候再次循环是无法读取文件内容的,需要使用 rewind() 函数将游标进行重置。

读取单个字符

rewind($f);
while (($c = fgetc($f)) !== false) {
echo $c, PHP_EOL;
}
// R
// a
// i
// n // i
// s // f
// a
// ……
// ……

fgetc() 函数用于读取单个字符。这个函数就比较简单了,不过需要注意的是如果用它读取中文的话,效果就不行了,因为中文是一个字占 2 或 3 个字节,使用这个函数读取出来的将是乱码的内容,在后面我们会有示例。

读取一行

while (($c = fgets($f)) !== false) {
echo $c, PHP_EOL;
}
// Rain is falling all around, // It falls on field and tree, // It rains on the umbrella here, // And on the ships at sea.

fgets() 函数用于按行读取文件内容,这个函数也是比较常用的函数,大家不会陌生。

读取 csv 文件

// fgetcsv
$f = fopen('./csv_test.csv', 'r');
while (($c = fgetcsv($f)) !== false) {
print_r($c);
}
// Array
// (
// [0] => 49
// [1] => 20
// [2] => 0
// [3] => 42
// [4] => 5/10/2020 12:32:18
// )
// Array
// (
// [0] => 50
// [1] => 21
// [2] => 0
// [3] => 74
// [4] => 5/10/2020 12:32:29
// )
// Array
// (
// [0] => 51
// [1] => 22
// [2] => 0
// [3] => 35.8
// [4] => 5/10/2020 12:32:38
// )
// ……
// ……
fclose($f);

关于 CSV 是什么文件这里就不多做解释了,笔者毕业时的第一个项目中就有很多操作 CSV 文件的小功能,也可以说,这个 fgetcsv() 函数是笔者对于文件操作的启蒙函数。它可以方便地按行读取 CSV ,并将它们解析成数组格式方便我们地操作。不过一般如果是 Excel 文件转换过来的内容,我们都会将第一行标题行排除掉,当然,这个就是根据业务开发的实际情况来说啦。

读取过滤HTML

// fgetss
$f = fopen('./html_test.txt', 'r');
while (($c = fgetss($f)) !== false) {
echo $c, PHP_EOL;
}
// PHP Deprecated: Function fgetss() is deprecated
fclose($f);

fgetss() 函数在读取文件的时候可以过滤掉 HTML 代码,不过这个函数已经废弃了。

中文读取问题

对于中文的读取来说,我们最主要关心的就是中文字符和英文字符所占字节的区别问题,上面已经说过了,中文如果是 UTF8 编码格式,将占用 3 个字节,如果是 GBK 之类的将占用 2 个字节。所以如果我们使用 fread() 时,要使用对应编码的倍数来读取,比如下面我们的测试文件是 UTF8 编码的,需要按三个字符的方式读取,就需要传递参数为 6 。

// 中文测试
$f = fopen('./cn_test.txt', 'r+'); while (!feof($f)) {
$contents = fread($f, 6);
echo $contents, PHP_EOL;
}
// 我本
// 无为
// 野客
// ,飘
// 飘浪
// 迹人
// 间。 // 一�
// �被�
// ……
// …… while (!feof($f)) {
$contents = fread($f, 1024);
echo $contents, PHP_EOL;
}
// rewind($f);
while (!feof($f)) {
$contents = fread($f, 1024);
echo $contents, PHP_EOL;
}
// 我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。
// 流霞细酌咏诗篇。且与白云为伴。 rewind($f);
while (($c = fgetc($f)) !== false) {
echo $c, PHP_EOL;
}
// �
// �
// �
// ……
// …… rewind($f);
while (($c = fgets($f)) !== false) {
echo $c, PHP_EOL;
}
// 我本无为野客,飘飘浪迹人间。 // 一时被命住名山。未免随机应变。 // 识破尘劳扰扰,何如乐取清闲。 // 流霞细酌咏诗篇。且与白云为伴。 fclose($f);

fread() 函数读取的内容中间为什么还会出现乱码呢?因为我们的换行符还是按英文码只占一个字节的呀!另外,fgetc() 函数就比较惨了,fgets() 函数还是能够正常地读取地。

读取剩余内容

$f = fopen('./cn_test.txt', 'r+');

echo fgets($f), PHP_EOL;
// 我本无为野客,飘飘浪迹人间。 echo fpassthru($f), PHP_EOL;
// 一时被命住名山。未免随机应变。
// 识破尘劳扰扰,何如乐取清闲。
// 流霞细酌咏诗篇。且与白云为伴。 rewind($f);

在这段测试代码中,我们使用 fgets() 读取了一行内容,然后再使用 fpassthru() 直接就将文件中剩余的内容全部读取出来了。这就是 fpassthru() 函数的作用,它可以将文件中游标之后全部剩余的内容读取出来。

fseek($f, 3 * 14 + 1);
echo fgets($f), PHP_EOL;
// 一时被命住名山。未免随机应变。

另外还有一个 fseek() 函数,可以指定当前从哪个位置开始读取,可以将它也看做是游标操作的一部分。

rewind($f);
fseek($f, 14 * 2 * 3 + 1);
echo ftell($f), PHP_EOL; // 85

ftell() 函数则是返回的文件剩余的字节信息。

文件句柄信息

print_r(fstat($f));
// Array
// (
// [0] => 16777220
// [1] => 8708492112
// [2] => 33188
// [3] => 1
// [4] => 501
// [5] => 20
// [6] => 0
// [7] => 177
// [8] => 1603414680
// [9] => 1603414679
// [10] => 1603414679
// [11] => 4096
// [12] => 8
// [dev] => 16777220
// [ino] => 8708492112
// [mode] => 33188
// [nlink] => 1
// [uid] => 501
// [gid] => 20
// [rdev] => 0
// [size] => 177
// [atime] => 1603414680
// [mtime] => 1603414679
// [ctime] => 1603414679
// [blksize] => 4096
// [blocks] => 8
// )

fstat() 函数和之前文章中我们讲过的 stat() 函数的功能是一样的,只不过它需要的是一个句柄参数,然后返回这个句柄对应文件的信息。而 stat() 直接给的是文件的路径。

文件截断

// 文件会变
ftruncate($f, 14*2*3+4);
echo fread($f, 8094), PHP_EOL;
// 我本无为野客,飘飘浪迹人间。
// 一时被命住名山。未免随机应变。
fclose($f);

ftruncate() 函数会从指定的位置截断文件内容。在这里我们只保留了前两行的内容,后面的内容就被截断掉了。使用这个函数需要注意的是,它会改变原有文件的内容。

读取文件并从格式化输入

$f = fopen("users_test.txt", "r");
while ($userinfo = fscanf($f, "%s\t%s\t%s\n")) {
print_r($userinfo);
}
// Array
// (
// [0] => javier
// [1] => argonaut
// [2] => pe
// )
// Array
// (
// [0] => hiroshi
// [1] => sculptor
// [2] => jp
// )
// Array
// (
// [0] => robert
// [1] => slacker
// [2] => us
// )
// Array
// (
// [0] => luigi
// [1] => florist
// [2] => it
// )
fclose($f);

fscanf() 函数会根据第二个参数传递的内容来格式化文件的内容。就像会用 printf() 函数一样,只不过它是从读取的角度来获得数据内容。这里会将制表符作为分隔来形成格式化的结果数组。

文件内容匹配

var_dump(fnmatch('*fall[ing]*', file_get_contents('./test.txt'))); // bool(true)

fnmatch() 函数用于判断给定的内容中是否包含第一个参数中指定的规则。它有点像正则表达式相关的函数的用法,而且并不是操作文件的,是针对字符串的。不过它的规则定义是以 Linux 系统中的文件操作匹配规则为准的,也就是说它不是完全的正则规则。就像我们经常在 Linux 中查看某个文件的信息:ll *.txt 这样。

进程文件读取操作

这个是什么意思呢?其实就是我们可以执行一段操作系统的进程代码,然后获得它的结果,这个流会以文件流的形式返回给 PHP 形成一个文件流句柄。

$handle = popen("/bin/ls", "r");
while(!feof($handle)){
echo fgets($handle);
}
pclose($handle);
// 1.PHP中的日期相关函数(三).php
// 2.学习PHP中的目录操作.php
// 3.学习PHP中的高精度计时器HRTime扩展.php
// 4.PHP中DirectIO直操作文件扩展的使用.php
// 5.学习PHP中Fileinfo扩展的使用.php
// 6.PHP中的文件系统函数(一).php
// 7.PHP中的文件系统函数(二).php
// 8.PHP中的文件系统函数(三).php
// cn_test.txt
// csv_test.csv
// html_test.txt
// test.txt
// timg.jpeg
// users_test.txt
// write.txt

文件写入

文件写入就比较简单了,就这么一点代码的介绍。而且也就三个函数。

// 写入
$f = fopen('write.txt', 'w');
fwrite($f, "This is Test!\n");
fputs($f, "This is Test2!!\n");
$csv = [['id', 'name'],[1, 'Zyblog'], [2, '硬核项目经理']];
foreach($csv as $v){
fputcsv($f, $v);
}
fclose($f);
// This is Test!
// This is Test2!!
// id,name
// 1,Zyblog
// 2,硬核项目经理

fwrite() 用于向文件句柄中写入内容。fputs() 是 fwrite() 的别名,它们两个是一个东西。fputcsv() 函数则是以 CSV 的格式将数组内容写入到文件中,它还有其它的参数可以修改分隔符具体使用哪个符号,在这里我们默认就是逗号。

文件加锁

$fp = fopen("/tmp/lock.txt", "w+");

if (flock($fp, LOCK_EX)) { // 进行排它型锁定
fwrite($fp, "写入数据:" . date('H:i:s') . "\n");
if(!$argv[1]){
sleep(50);
}
fflush($fp); // 释放锁之前刷新缓冲区
flock($fp, LOCK_UN); // 释放锁定
} else {
echo "无法获得锁,不能写入!";
} fclose($fp);

锁定一个文件,然后其它的操作就不能读取它了,这种操作一般在多线程或者多个功能会同时操作一个文件时会非常常用。flock() 的第二个参数可以设置读锁、写锁等,这里我们使用的是 LOCK_EX 共享排它锁,也就是一个写锁。当我们运行这段代码后,在停留的时间内容,其它的脚本是无法写入数据的,如果有同时操作这个文件的脚本在运行也会卡在这里直到这边的锁释放掉。

  • LOCK_SH 取得共享锁定(读取的程序)。
  • LOCK_EX 取得独占锁定(写入的程序。
  • LOCK_UN 释放锁定(无论共享或独占)。
  • 如果不希望 flock() 在锁定时堵塞,则是 LOCK_NB(Windows 上还不支持)。

fflush() 用于刷新缓冲区,这个也是之前讲过的关于 PHP 中缓冲区相关的知识,大家可以回去温习一下,PHP中的输出缓冲控制。在文件操作中,使用这个函数就能马上刷新缓冲区的内容并将内容写入到具体的文件中。

总结

是不是很嗨,一下子学习了这么多函数。这篇文章结束也就是 PHP 原生的这些文件操作函数就学习完了。当然,了解只是一方面,更多的还是要多多尝试应用到自己的项目中。

测试代码:

https://github.com/zhangyue0503/dev-blog/blob/master/php/202010/source/8.PHP中的文件系统函数(三).php

参考文档:

https://www.php.net/manual/zh/ref.filesystem.php

关注公众号:【硬核项目经理】获取最新文章

添加微信/QQ好友:【xiaoyuezigonggong/149844827】免费得PHP、项目管理学习资料

知乎、公众号、抖音、头条搜索【硬核项目经理】

B站ID:482780532

PHP中的文件系统函数(三)的更多相关文章

  1. PHP中的文件系统函数(二)

    这次我们来学习的是一些不是太常用,但却也非常有用的一些函数.它们中有些大家可能见过或者使用过,有一些可能就真的没什么印象了.它们都是 PHP 中文件系统相关操作函数的一部分.存在即合理,或许只是我们的 ...

  2. PHP中的文件系统函数(一)

    从这篇文章开始,我们将学习一系列的 PHP 文件系统相关函数.其实这些函数中,有很多都是我们经常用到的,大家并不需要刻意地去记住它们,只要知道有这么个东西,在使用的时候记得来查文档就可以了. 文件路径 ...

  3. golang中的匿名函数三种用法

    package main import ( "fmt" "strconv" ) func main() { // 匿名函数的使用:方式1 f1 := func( ...

  4. javascript基础程序(算出一个数的平方值、算出一个数的阶乘、输出!- !- !- !- !- -! -! -! -! -! 、函数三个数中的最大数)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. C#中的函数(三)参数传递及返回值

    接前面二篇,继续开始新的研究 前面忘了说什么是主调函数与被调函数 主调函数:执行调用其它函数语句所在的函数 被调函数:被其它函数所调用的函数 简单说就是一个是发起调用者,另一个是被调用者 写个小例子说 ...

  6. PHP:第三章——PHP中的可变函数

    PHP中的可变函数 <?php header("Content-Type:text/html;charset=utf-8"); function F(){ echo '999 ...

  7. C中不安全函数

    C 中大多数缓冲区溢出问题可以直接追溯到标准 C 库.最有害的罪魁祸首是不进行自变量检查的.有问题的字符串操作(strcpy.strcat.sprintf 和 gets).一般来讲,象“避免使用 st ...

  8. 在nodeJS中操作文件系统(二)

    在nodeJS中操作文件系统(二)   1. 移动文件或目录 在fs模块中,可以使用rename方法移动文件或目录,使用方法如下:     fs.rename(oldPath,newPath,call ...

  9. 在Node.js中操作文件系统(一)

    在Node.js中操作文件系统 在Node.js中,使用fs模块来实现所有有关文件及目录的创建,写入及删除操作.在fs模块中,所有对文件及目录的操作都可以使用同步与异步这两种方法.比如在执行读文件操作 ...

随机推荐

  1. Asp.Net Core 使用 Sqlite 数据库

    在Asp.Net Core 使用 Sqlite 数据库 在Asp.Net Core(5.0)项目中使用Sqlite数据库的设置, 1,创建新web项目 2,安装下面的依赖包 Install-Packa ...

  2. Pytest+Allure 示例

    0. 前言 简介 Allure 框架是一个灵活的.轻量级的.支持多语言的测试报告工具,它不仅以 Web 的方式展示了简介的测试结果,而且允许参与开发过程的每个人可以从日常执行的测试中,最大限度地提取有 ...

  3. Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile

    一.问题由来 下午的时候,电脑用得好好的,突然一下死机,么办法只能够重新启动.再次打开IDEA的时候,之前打开的所有的项目 信息都不在了,我重新打开项目,然后就出现问题,所有的类都报红了,这让我很是意 ...

  4. NOIP 模拟 $16\; \rm Star Way To Heaven$

    题解 \(by\;zj\varphi\) 看懂题!!! 从最左穿到最右,一定会经过两个星星之间或星星和边界之间,那么我们穿过时当前最优一定是走中点 而我们要求最小的距离最大,那么我们将所有星星和边界( ...

  5. 题解 Connect

    传送门 各种骗分无果,特殊性质还手残写挂了-- 首先完全图上直接输出边权 \(\times (n-2)\) 就行了,然而我脑残乘的 \(n-1\) 看数据范围肯定是状压,但是压边肯定炸了,考虑压点 因 ...

  6. redis搭建集群和主从

    说明 Redis集群至少需要3个节点,来支持投票容错机制,每个节点都有从节点,所有最少是6个服务(3个主3个从) 因为集群内置了16384个slot(哈希槽),并且把所有的物理节点映射到了这16384 ...

  7. java实现随机字母数字验证码

    生成随街验证码 VerifyCode 工具类 package com.meeno.common.cerifycode; import javax.imageio.ImageIO; import jav ...

  8. MySQL 数据库、数据表、数据的基本操作

    1.数据库(database)管理 1.1 create 创建数据库 create database firstDB; 1.2 show 查看所有数据库 mysql> show database ...

  9. wpf 自定义 RadioButton.

    <Style TargetType="RadioButton" x:Key="nav"> <Setter Property="Tem ...

  10. C#串口通信SeriPort 电表DLT645 RS234/RS485

    难受,三个多月前有一个电表电量监控的项目.做完了就没再管了.今天有需求需要改一些地方,但是....我想不起来干了啥,怎么干的啦.真的完全忘了.....项目名称叫啥都忘了.找了半天 不知道有没有和我一样 ...