-
- 前言
- 遍历文件
- scandir
- 给定目录
- 非法使用
- dir函数
- 递归法
- scandir
- 路径解析
- 数据结构设计
- 原理解析
- 代码实现
- 演示
- 当前目录
- 父级目录
- 总结
前言
这几天在看easyui,看到树形结构这个组件的时候突发奇想,能不能把站点以目录树的形式展示呢?
然后着手实现了一下,具体的来说是实现了对数据层的获取,还没有附加到tree组件上。下面就来谈谈我对这次文件信息抓取的体会吧。
遍历文件
在PHP中遍历文件有很多方式,但是适用的场景不尽相同。所以在合适的场合适用合适的方法显得至关重要,下面简要的了解一下。
scandir
如果说想找到一款类似于Python中使用os.walk
获取文件目录信息的优雅的方法,在PHP中就不是那么的方便了,唯一能称得上简单的就是scandir
函数,但是这个函数并不优雅,其作用就是扫描给定目录下的文件信息(如果包含子目录,那就只能显示到子目录的层级,再想查看子目录下的信息,那就不行了,否则会报错的)。
空口无凭,找个实例来看一下就一目了然了。
给定目录
<?php
/**
* Created by PhpStorm.
* User: ${郭璞}
* Date: 2017/2/3
* Time: 20:25
* Description: scandir函数测试
*/
$pathinfo = scandir(‘.‘);
var_dump($pathinfo);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
效果如下
非法使用
所谓非法使用,就是指给出非目录文件时的场景,比如我们直接给个文件的路径,就是这样了。
<?php
/**
* Created by PhpStorm.
* User: ${郭璞}
* Date: 2017/2/3
* Time: 20:25
* Description: scandir函数测试
*/
$pathinfo = scandir(‘./scandir.php‘);
var_dump($pathinfo);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
所以,使用scandir函数的时候务必明确这一点,传正确的参数!!!
dir函数
既然使用scandir函数不行了,那咱们就换个思路呗。下面介绍一个比较常用的方法。
<?php
/**
* Created by PhpStorm.
* User: ${郭璞}
* Date: 2017/2/3
* Time: 20:25
* Description: scandir函数测试
*/
$path = ".";
if(is_dir($path)) {
$dirinfo = dir($path);
while($file = $dirinfo->read()) {
echo "<mark>".$file."</mark><br />";
}
$dirinfo->close();
}else if (is_file($path)) {
echo "<font color=‘green‘>".$path."</font>";
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
如果将$path=‘.‘
换成$path=‘./scandir.php‘
。将出现如下结果。
当然,这两个方法都没能实现我们想要的效果。不能突破子目录的情况,没办法遍历到最底层的文件信息。
递归法
既然如此,那就得另寻他法了。我个人觉得递归的方法不赖,应该可以灵活地处理这些问题,说做就做。
使用面向过程的PHP编码方法需要处理外部数组引用问题,显得代码不是很容易理解,所以我选择面向对象的方法,将外部数组封装到一个类中,专门用于处理这类问题。
<?php
/**
* Created by PhpStorm.
* User: ${郭璞}
* Date: 2017/2/3
* Time: 9:32
* Description: 读取给定目录及子目录下文件路径信息
*/
class FileWatcher{
public $fileinfo;
/**
* FileWatcher constructor.
* @param $path 给定路径
*/
function __construct(){
$this->fileinfo = array();
}
/**
* 去除路径设置信息,析构方法
*/
function __destruct()
{
// TODO: Implement __destruct() method.
$this->fileinfo = null;
}
public function scanDir($path) {
if(is_dir($path)) {
$tmpdir = dir($path);
while($tmpfile = $tmpdir->read()) {
if($tmpfile!=‘.‘ && $tmpfile!=‘..‘)
$this->scanDir($path."/".$tmpfile);
}
$tmpdir->close();
}
if(is_file($path)) {
array_push($this->fileinfo, $path);
}
return $this->fileinfo;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
下面是测试时使用的代码。
$fileWatcher = new FileWatcher();
$result = $fileWatcher->scanDir(‘.‘);
var_dump($result);
- 1
- 2
- 3
- 4
最终实现的效果为:
路径解析
单单是这样,不是很好用。我就想着能不能实现类似于Python中os.walk
那样优雅的获取相关的信息呢?
数据结构设计
使用过那个方法的应该都了解,获取到的元组信息非常的详细,包括路径啊,目录级啊什么的非常的详细。
但是我这边为了以后使用easyui的tree组件,可能需要处理一下目录深度的问题,所以我设计了下面的数据结构。比较简单,但是实用性感觉还是挺强的。
class FileInfo{
// 目录深度
public $level;
// 文件经过的路径,以数组形势依次填充
public $pathstep;
// 文件的完整路径
public $fullpath;
public function __construct()
{
//pathstep 存储当前路径经过的文件夹信息
$this->pathstep = array();
}
public function __destruct()
{
// TODO: Implement __destruct() method.
$this->pathstep = null;
$this->level = null;
$this->fullpath = null;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
原理解析
我个人认为原理还是比较简单的了,那就是以文件分隔符作为计算标准。当然了,需要处理一大堆的路径适配问题,尤其是./
和../
这样的相对路径。
处理完这些之后就轻松多了,使用explode
函数将字符串进行分割,装填到数组中即可。
代码实现
class PathParser{
private $patharray;
private $resultSet;
public function __construct($patharray)
{
// 从外部获取到处理结果集
$this->patharray = $patharray;
// 初始化结果集数组
$this->resultSet = array();
// bean类对象
$this->fileinfo = new FileInfo();
}
public function __destruct()
{
// TODO: Implement __destruct() method.
$this->resultSet = null;
$this->level = null;
$this->fullpath = null;
}
public function parse() {
for ($index=0; $index<count($this->patharray); $index++) {
// 赋予完整路径
$fileinfo = new FileInfo();
$fileinfo->fullpath = $this->patharray[$index];
//计算level
$fileinfo->level = $this->parseLavel($fileinfo->fullpath);
// echo $fileinfo->level."<------->";
// 计算经过的路径并进行存储
$fileinfo->pathstep = $this->parseStep($fileinfo->fullpath);
// echo $fileinfo->pathstep."<br />";
// var_dump($fileinfo->pathstep);
array_push($this->resultSet, $fileinfo);
}
//返回计算结果,整体作为结果集返回
return $this->resultSet;
}
/**
* 获取给定路径所经过的路径的结果集,将用于分级目录展示
* @param $fileinfo
* @return int
*/
public function parseStep($fileinfo) {
if(!$fileinfo) {
echo "<mark>".$fileinfo." path error!</mark>";
exit();
}
// 判断是否为相对路径是的话去掉第一级目录。 啊好烦,windows上和linux上差别还这么大,怎么处理好呢。。。
// 还是按照文件在服务器上的位置来进行来处理好了。判断是不是相对路径然后再针对“路径分隔符”计算路径的level
if($this->isRelativePath($fileinfo) == 1) {
// 相对路径处理
// 去掉相对路径符号
$fileinfo = substr($fileinfo,2, strlen($fileinfo));
// 按照目录分隔符 作为切割标准,结果就是路径本身包含的路径信息
return explode("/", $fileinfo);
}else if($this->isRelativePath($fileinfo) == 2){
$fileinfo = substr($fileinfo, 3, strlen($fileinfo));
return explode("/", $fileinfo);
}else if ($this->isAbsolutePath($fileinfo)) {
// 绝对路径处理
}else{
// 文件路径非法
echo "<mark>".$fileinfo." 文件路径非法</mark>";
exit();
}
}
public function parseLavel($fileinfo) {
if(!$fileinfo) {
echo "<mark>".$fileinfo." path error!</mark>";
exit();
}
//按照文件在服务器上的位置来进行来处理好了。判断是不是相对路径然后再针对“路径分隔符”计算路径的level
if($this->isRelativePath($fileinfo) == 1) {
// 相对路径处理
// 去掉当前相对路径符号
$fileinfo = substr($fileinfo,2, strlen($fileinfo));
// 通过计算 路径分隔符来作为level的判断标准
// echo "<mark>".count(explode("/", $fileinfo))."</mark>";
return count(explode("/", $fileinfo));
}else if ($this->isRelativePath($fileinfo) == 2 ) {
//去掉父级目录信息
$fileinfo = substr($fileinfo, 3, strlen($fileinfo));
return count(explode("/", $fileinfo));
}else if ($this->isAbsolutePath($fileinfo)) {
// 绝对路径处理
// 算了,先不做这块了,貌似偏离了我这个需求。
}else{
// 文件路径非法
echo "<mark>".$fileinfo." 文件路径非法</mark>";
exit();
}
}
/**
* 判断是否为相对路径
* @param $path
* @return bool
*
*/
private function isRelativePath($path) {
// 父级目录拥有更高的优先级
$prefix = substr($path, 0, 3);
if($prefix == "../") {
return 2;
}
// 处理 当前目录情况
$prefix = substr($path, 0, 2);
if ($prefix == "./"){
return 1;
}else{
return false;
}
}
/**
* 判断给定路径是否为绝对路径
* @param $path
* @return bool
*/
private function isAbsolutePath($path) {
$prefix = substr($path, 0, 1);
if($prefix=="/"){
return true;
}else{
return false;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
演示
下面演示一下实现的效果吧。
当前目录
测试代码如下
//获取全部文件以及路径信息
$fileWatcher = new FileWatcher();
$result = $fileWatcher->scanDir(‘.‘);
$pathParser = new PathParser($result);
$resultSet = $pathParser->parse();
echo json_encode($resultSet);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
结果图
父级目录
对于父级目录信息获取,也是非常方便的。之前网上下载了easyui的压缩包,解压后扔到了apache服务器上,下面来看看对这个大文件信息集的获取情况吧。
测试代码把路径中的那个.
改成../easyui
即可。
结果还行吧。我看着挺详细的了。那么到这里就差不多实现预期的效果了。
总结
回顾一下,今天主要是对于文件目录信息的遍历。
显示通过通用的scandir 函数和dir循环读取方式对目录进行了读取,但是效果不佳,于是转战递归实现。
为了达到一个更加优雅的信息获取效果,又设计了一个专门针对于文件的类,用于存储相关数据。
为了处理相对路径中本级目录和父级目录等特殊情况,又使用了substr和explode函数,最后封装成了一个通用的类,效果还不错。
缺点嘛,显而易见。代码的风格不是很好,命名什么的也是按照我自己的套路来的,不是很正规。
其他的貌似也没什么了,那就先这样好了。
再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow
原文地址:https://www.cnblogs.com/djuwcnhwbx/p/10325861.html