导语####
任何类型的变量,都是在程序运行期间才将数据加载到内存中的,并不能持久保存。如果需要将数据长久保存起来,以便后期程序再次运行时还可以使用,存储的基本方法通常有两种:将需要持久化的数据保存到普通文件或数据库中。而对文件的处理因为比较繁琐,所以并不是用来持久储存数据的首选,但在任何计算机设备中,文件都是必需的对象,尤其是在Web编程中,文件的操作是非常有用的,我们可以在客户端通过访问PHP脚本程序,动态地在Web服务器上生成目录,创建、编辑、修改文件,像开发采集程序、网页静态化、文件上传及下载等操作都离不开文件处理。
一、文件####
1、文件类型
PHP是以UNIX的文件系统为模型的,因此在Windows系统中我们只能获得"file"、"dir"、"unknown"三种文件类型。而在UNIX系统中,我们可以获得"block"、"char"、"dir"、"fifo"、"file"、"link"或"unknown"7种文件类型。
fileType: 获取文件的类型,该函数接受一个文件名为参数,如果文件不存在返回false。
2、文件属性
函数名 | 作用 | 参数 | 返回值 |
---|---|---|---|
file_exists() | 检查文件是否存在 | 文件名 | 文件存在返回TRUE |
filesize() | 取得文件大小 | 文件名 | 返回文件大小的字节数 |
is_readable() | 判断文件名是否可读 | 文件名 | 存在且可读返回TRUE |
is_writable() | 判断文件名是否可写 | 文件名 | 存在且可读写返回TRUE |
is_executable() | 判断文件名是否可执行 | 文件名 | 存在且可执行返回TRUE |
filectime() | 获取文件的创建时间 | 文件名 | 返回UNIX时间戳格式 |
filemtime() | 获取文件的修改时间 | 文件名 | 返回UNIX时间戳格式 |
fileatime() | 获取文件的访问时间 | 文件名 | 返回UNIX时间戳格式 |
stat() | 获取文件的大部分属性 | 文件名 | 返回文件有用信息的数组 |
代码示例:
/*声明一个函数,通过传入一个文件名获取文件的部分属性*/
function getFilePro($fileName){
//如果文件或目录不存在,则直接退出目录
if(!file_exists($fileName)){
echo "目标文件不存在<br/>";
return;
}
//判断是否是一个普通文件
if(is_file($fileName)){
echo $fileName . "是一个文件<br/>";
}
//判断是否是一个目录
if(is_dir($fileName)){
echo $fileName . "是一个目录<br/>";
}
//用定义的函数输出文件类型
echo "文件类型:" . getFileType($fileName) . "<br/>";
//用定义的函数输出文件大小
echo "文件大小:" . getFileSize(fileSize($fileName)) . "<br/>";
//判断文件是否可读
if(is_readable($fileName)) echo "文件可读<br/>";
//判断文件是否可写
if(is_writable($fileName)) echo "文件可写<br/>";
//判断文件是否可以执行
if(is_executable($fileName)) echo "文件可执行<br/>";
echo "文件创建时间:" . date("Y年m月j日",filectime($fileName)) . "<br/>;
echo "文件最后修改时间:" . date("Y年m月j日",filemtime($fileName)) . "<br/>";
echo "文件最后打开时间:" . date("Y年m月j日",fileatime($fileName)) . "<br/>";
/**声明一个函数,用来返回文件的类型**/
function getFileType($fileName){
switch(fileType($fileName)){
case 'file' : $type = "普通文件"; break;
case 'dir' : $type = "目录文件"; break;
case 'block' : $type = "块设备文件"; break;
case 'char' : $type = "字符设备文件"; break;
case 'fifo' : $type = "命名管道文件"; break;
case 'link' : $type = "符号链接"; break;
case 'unknown : $type = "未知文件"; break;
default : $type = "没有检测到文件类型"; break;
}
return $type;
}
}
/**声明一个函数,用来返回文件的大小**/
function getFileSize($bytes){
if($bytes >= pow(2,40)){
$res = round($bytes / pow(1024,4),2);
$suffix = "TB";
}else if($bytes >= pow(2,30)){
$res = round($bytes / pow(1024,3),2);
$suffix = "GB";
}else if($bytes >= pow(2,20)){
$res = round($bytes / pow(1024,2),2);
$suffix = "MB";
}else if($bytes >= pow(2,10)){
$res = round($bytes / pow(1024,1),2);
$suffix = "KB";
}else{
$res = $bytes;
$suffix = "Bytes";
}
return $res . " " . $suffix;
}
二、目录####
1、解析目录路径
- 绝对路径:从根目录开始一级一级地进入各个子目录,最后指定改文件名或目录名。
- 相对路径:从当前目录进入某目录,最后指定改文件名或目录名。
在系统的每个目录下都有两个特殊的目录“.”和“..”,分别指示当前目录和当前目录的父目录
在UNIX系统和Windows系统中,目录分隔符是不同的,在UNIX系统中必须使用正斜线“/”作为路径分隔符,而在Windows系统中默认使用反斜线“\”作为路径分隔符,在程序中表示时还要将“\”转义,但也接受正斜线“/”的写法。建议都是用“/”作为文件的路径分隔符。另外,也可以使用PHP内置常量DIRECTORY_SEPARATOR,其值为当前操作系统的默认文件路径分隔符。
下面为几个常用的函数:
basename():返回路径中的文件名部分
dirname():返回去掉文件名后的目录名
pathinfo():返回一个关联数组,包含路径中的目录名、基本名、扩展名
2、遍历目录
opendir():打开指定目录,接受一个目录的路径及目录名作为参数,函数返回值为可供其他目录函数使用的目录句柄(资源类型)。如果该目录不存在或没有访问权限,则返回false。
readdir():用于读取指定目录,接受已经用opendir()打开的卡操作目录句柄作为参数,函数返回当前目录指针位置的一个文件名,并将目录指针向后移动一位。当指针位于目录的结尾使,因为没有文件存在则返回false。
closedir():关闭指定目录,接受已经用opendir()打开的可操作目录句柄作为参数,函数无返回值,运行后将关闭打开的目录。
rewinddir():倒回目录句柄,接受已经用opendir()打开的可操作目录句柄作为参数,将目录指针重置目录到开始处,即倒回目录的开头。
代码示例:
/****完整的遍历目录下所有的指定文件类型函数****/
function traverse($path = '.') {
$current_dir = opendir($path); //opendir()返回一个目录句柄,失败返回false
while(($file = readdir($current_dir)) !== false) { //readdir()返回打开目录句柄中的一个条目
//$sub_dir = $path . DIRECTORY_SEPARATOR . $file; //构建子目录路径 DIRECTORY_SEPARATOR 代表反斜线 \
$sub_dir = $path . '/' . $file;
if($file == '.' || $file == '..') {
continue;
} else if(is_dir($sub_dir)) { //如果是目录,进行递归
echo 'Directory ' . $file . ':<br>';
traverse($sub_dir);
} else { //如果是文件,直接输出
echo 'File in Directory ' . $path . ': ' . $file . '<br>';
}
}
}
traverse('../../');
3、统计目录大小
代码示例:
/**自定义一个函数,用于统计传入参数的目录大小**/
function getDirSize($dirName){
$dir_size = 0; //用来累计各个文件大小
if($dir_handle = opendir($dirName)){
while(($file = readdir($dir_handle)) !== false){
if($file == '.' || $file == '..') continue; //排除两个特殊目录
$subFile = $dirName . DIRECTORY_SEPARATOR . $file; //将目录下的子文件和当前目录相连
if(is_dir($subFile)){
$dir_size += getDirSize($subFile); //递归地调用自身函数,求子目录的大小
}else{
$dir_size += filesize($subFile);
}
}
closedir($dir_handle);
}
return $dir_size;
}
4、建立和删除目录
mkdir():创建目录【以最高权限创建目录 mkdir(目录名,0777,true)】
rmdir():删除目录,只能删除一个空目录并且目录必须存在
自定义递归函数删除目录的示例代码:
function delDir($dir){
if(file_exists($dir)){ //如果不存在rmdir(0函数会出错
if($dir_handle = @opendir($dir)){ //打开目录并判断是否成功
while(($file = readdir($dir_handle)) !== false){ //循环便利目录
if($file == '.' || $file == '..') continue; //一定要排除两个特殊的目录
$subFile = $dir . DIRECTORY_SEPARATOR . $file; //将目录下的文件和当前目录相连
if(is_dir($subFile)){ //如果是目录则条件成立
delDir($subFile); //递归
}else{
unlink($subFile); //删除文件
}
}
closedir($dir_handle);
rmdir($dir); //删除空目录
}
}
}
5、复制目录
function copyDir($dirSrc,$dirTo){
if(is_file($dirTo)){
echo "目标不是目录不能创建";
return;
}
if(!file_exists($dirTo)){
mkdir($dirTo,0777,true);
}
if($dir_handle = @opendir($dirSrc)){
while(($file = readdir($dir_handle)) !== false){
if($file == '.' || $file == '..') continue;
$subSrcFile = $dirSrc .DIRECTORY_SEPARATOR . $file;
$subToFile = $dirTo . DIRECTORY_SEPARATOR . $file;
if(is_dir($subSrcFile)){
copyDir($subSrcFile,$subToFile);
}
if(is_file($subSrcFile)){
copy($subSrcFile,$subToFile);
}
}
closedir($dir_handle);
}
}
copyDir('vendor','photos/vendor');
三、文件的基本操作####
1、文件的打开与关闭
- fopen():打开文件,建立与文件资源的连接,并使文件指针指向文件。
- fclose():关闭文件,即断开文件指针与文件之间的联系。
在fopen()中第二个参数可以使用的文件模式
模式 | 描述 |
---|---|
r | 只读方式打开文件,从文件开头开始读 |
r+ | 读写方式打开文件,从文件开头开始读写 |
w | 只写方式打开文件,从文件开头开始写。如果文件已经存在,将文件指针指向文件头并将文件大小截为零,即删除所有文件已有的内容。如果文件不存在,函数将创建这个文件 |
w+ | 读写方式打开文件,从文件头开始读写。如果文件已经存在,将文件指针指向文件头并将文件大小截为零,即删除所有文件已有的内容。如果该文件不存在则尝试创建它,仅能用于本地文件 |
x | 创建并以写入方式打开,将文件指针指向文件头。如果文件已经存在,则fopen()调用失败并返回false,并生成一条E_WARNING级别的错误信息。如果文件不存在则尝试创建它,仅能用于本地文件 |
x+ | 创建并以读写方式打开,将文件指针指向文件头。如果文件已经存在,则fopen()调用失败并返回false,并生成一条E_WARNING级别的错误信息。如果文件不存在则尝试创建它,仅能用于本地文件 |
a | 写入方式打开,将文件指针指向文件末尾。如果该文件已有内容,将从该文件末尾开始追加。如果该文件不存在,函数将创建这个文件 |
a+ | 写入方式打开,将文件指针指向文件末尾。如果该文件已有内容,将从该文件末尾开始追加或读。如果该文件不存在,函数将创建这个文件 |
b | 以二进制模式打开文件,用于与其他模式进行连接。如果文件系统能够区分二进制文件和文本文件,你可能会用到它。例如在Windows系统中可以分区,而Unix系统则不分区。这个模式是默认的模式 |
t | 以文本模式打开文件,这个模式也只是Windows系统下的一个选项,不推荐使用 |
2、写入文件
fwrite(): 将字符串写入到文件中,参数1为fopen打开的文件资源,参数2为要写入的内容,参数3是写入到第length个 字符时停止写入。
代码示例:
$file = "demo.txt";
$handler = fopen($file,"r") or die('Could not open ' .$file); //以只读模式打开
//循环10次写入
for($i=0;$i<10;$i++){
fwrite($handler,$i ."-Hello world\n");
}
fclose($handler); //关闭指针资源
3、读取文件内容
读取文件的内容函数,如下表
函数 | 描述 |
---|---|
fread() | 读取打开的文件 |
file_get_contents() | 将文件读入字符串 |
fgets() | 从打开的文件中返回一行 |
fgetc() | 从打开的文件中返回字符 |
file() | 把文件读入一个数组中 |
readfile() | 读取一个文件,并输出到输出缓冲 |
fread()
feof():判断一个文件指针是否位于文件的结束处
代码示例:
$file = "test.txt";
$handle = fopen($file,"r") or die("文件打开失败");
$contents = fread($handle,50); //从文件读取前50个字节
echo $contents;
/**从文件中读取全部内容存入到一个变量中,每次读取一部分,循环读取**/
$file = "./photos/7141730.png"; //二进制文件
$handle = fopen($file,"rb") or die("文件打开失败");
$contents = "";
while(!feof($handle)){ //判断文件是否结尾
$contents .= fread($handle,1024);
}
fclose($handle);
echo $contents;
/**另一种从文件中读取全部内容的方法**/
$file = "test.txt";
$handle = fopen($file,"r") or die("文件打开失败");
$contents = fread($handle,filesize($file));
fclose($handle);
echo $contents;
file_get_contents() 如果只是将一个文件的内容读到一个字符串中,file_get_contents()性能要高一些
fgets()和fgetc()
代码示例:
$file = "./test.txt";
$handle = fopen($file,"r") or die("文件打开失败");
while(!feof($handle)){
$contents = fgets($handle,4096);
echo $contents . "<br/>"; //输出每一行
}
fclose($handle);
$handle = fopen($file,"r") or die("文件打开失败");
while(false !== ($char = fgetc($handle))){
echo $char . "<br/>"; //输出单个字符
}
fclose($handle);
file():与file_get_contents()类似,不需要使用fopen()打开文件。它可以把整个文件读入到一个数组中。数组中的每个元素对应文件中的相应的行,各元素由换行符分隔,同时换行符仍附加在每个元素的末尾。
代码示例:
echo "<pre>";
print_r(file("test.txt"));
echo "</pre>";
readfile():该函数可以读取指定的文件,立即输出到输出缓冲区,并返回读取的字节数,不需要使用fopen()打开文件。
代码示例:
readfile("test.txt");
4、访问远程文件
- 对远程文件进行读操作
代码示例:
//通过http打开远程文件 只读模式
$file = fopen("http://www.baidu.com","r") or die("打开远程文件失败");
//循环从文件中读取的内容
while(!feof($file)){
$line = fgets($file,1024); //每读取一行
//如果找到远程文件的标题标记则取出标题,并退出循环,不在读取文件
if(preg_match("/<title>(.*)<\/title>/",$line,$out)){
$title = $out[1];
break;
}
}
fclose($file);
echo $title;
- 对远程文件进行写操作
//在远程服务器上创建文件,以写的模式打开
$file = fopen("ftp://user:password@ftp.域名.net/path/file","w");
//将一个字符串写入到远程服务器中
fwrite($file,"hello world");
//关闭文件资源
fclose($file);
5、文件的锁定机制
多个客户端用户在同一时刻对服务器上的同一文件访问的情况下,如果一个用户正向文件中写入数据,当还没有写完时,其他用户在这一时刻也向这个文件中写入数据,就会造成数据写入混乱。还有,当用户没有将数据写完,其他用户就去获取这个文件的内容,也会得到残缺的数据。
flock():可以对文件使用锁定机制
代码示例:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<?php
//声明一个变量保存文件名,在这个文件中保存留言信息
$filename = "text_data.txt";
//判断用户是否按下提交按钮,用户提交后则条件成功
if(isset($_POST['sub'])){
//接收表单内容,并整合为一条,使用'||'分隔,使用'<|>'结尾
$message = $_POST['username'] . "||" . $_POST['title'] . "||" . $_POST['mess'] . "<|>";
writeMessage($filename,$message); //调用自定义函数,将信息写入文件
}
//判断文件是否存在,如果存在,读取数据
if(file_exists($filename)){
readMessage($filename);
}
/**
* 自定义一个函数,用于向文件中写入数据
* @param $filename 文件名
* @param $message 写入的内容
*/
function writeMessage($filename,$message){
$fp = fopen($filename,"a"); //以追加的模式打开文件
if(flock($fp,LOCK_EX)){ //进行排他型锁定(独占锁定)
fwrite($fp,$message); //写入文件
flock($fp,LOCK_UN); //释放锁定
}else{
echo "不能锁定文件";
}
fclose($fp); //关闭文件资源
}
/**
* 自定义函数,用来遍历读取文件
* @param $filename 文件名
*/
function readMessage($filename){
$fp = fopen($filename,"r"); //以只读模式打开文件
flock($fp,LOCK_SH); //建立文件的共享锁定
$buffer = "";
while(!feof($fp)){
$buffer .= fread($fp,1024); //读出数据后追加到$buffer变量中
}
$data = explode("<|>",$buffer); //将数据转换为数组,以"<|>"分隔
//遍历数组,以html格式输出
foreach($data as $line){
//将每行数据再分隔
@list($username,$title,$message) = explode("||",$line);
//判断每部分是否为空
if($username != "" && $title != "" && $message != ""){
echo $username . '说:';
echo ' '. $title. ', ';
echo $message . "<hr>";
}
}
flock($fp,LOCK_UN); //释放锁定
fclose($fp);
}
?>
<form action="" method="post">
用户名:<input type="text" name="username" size="10" id=""><br/>
标 题:<input type="text" name="title" size="30" id=""><br/>
<textarea name="mess" id="" cols="41" rows="4"></textarea>
<input type="submit" value="留言" name="sub">
</form>
</body>
</html>
6、文件的一些基本操作函数
函数 | 语法结构 | 描述 |
---|---|---|
copy() | copy(来源文件,目的文件) | 复制文件 |
unlink() | unlink(目标文件) | 删除文件 |
ftruncate() | ftruncate(目标文件资源,截取长度 | 将文件截取到指定长度 |
rename() | rename(旧文件名,新文件名) | 重命名文件或目录 |