PHP学习笔记之文件系统处理

导语####

任何类型的变量,都是在程序运行期间才将数据加载到内存中的,并不能持久保存。如果需要将数据长久保存起来,以便后期程序再次运行时还可以使用,存储的基本方法通常有两种:将需要持久化的数据保存到普通文件或数据库中。而对文件的处理因为比较繁琐,所以并不是用来持久储存数据的首选,但在任何计算机设备中,文件都是必需的对象,尤其是在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(旧文件名,新文件名) 重命名文件或目录
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 一、温故而知新 1. 内存不够怎么办 内存简单分配策略的问题地址空间不隔离内存使用效率低程序运行的地址不确定 关于...
    SeanCST阅读 7,774评论 0 27
  • PHP常用函数大全 usleep() 函数延迟代码执行若干微秒。 unpack() 函数从二进制字符串对数据进行解...
    上街买菜丶迷倒老太阅读 1,347评论 0 20
  • php usleep() 函数延迟代码执行若干微秒。 unpack() 函数从二进制字符串对数据进行解包。 uni...
    思梦PHP阅读 1,980评论 1 24
  • 欣賞別人是一種境界 老師讓我們寫自己欣賞的人,有幾位同學寫我。我也寫了一個人是我特別佩服的。 小A把小B的筆袋拉開...
    onlymi阅读 188评论 0 1