利用Dataview实现搜索指定目录下的所有文件,以指定的条件排序(可以按文件名升降序,最后修改时间升降序)
Posted on 2007-03-20 16:04 Nio 阅读(1126) 评论(0) 收藏 举报
利用Dataview实现搜索指定目录下的所有文件,以指定的条件排序(可以按文件名升降序,最后修改时间升降序)
在我的应用中,有这样一个案例,测试机为每个已测试的PCB板或者成品生成一份测试报表,为了得知它们的测试详细情况,需要实时捕获各测试条目的结果,所以在服务器上有一Parser分析这些文件,更新到数据库.当然也可以写一个dll给测试程序去调用及更新数据库,但测试机由不同的供应商提供,很难做到这点.
已知测试机有许多,有许多不同的测试工位,同一时间会产生大量的文件,相临工位有流程控制要求,即上一工位没有Pass记录,下一工位不允许通过.所以Parser要按时间的先后顺序来处理测试报表.查询MSDN帮助文档,并没有实现这个要求相关的类,所以利用DataView的SoryBy方法来实现它,实现的道理很简单,从源目录得到文件名及属性,构造一个DataTable,一列为文件名,另一列为最后修改时间,这样就可以用SortBy方法来处理它了,呵呵,有点取巧.
下面先来了解下C#中操作文件系统的类:
.Net中,有关文件系统操作的类都在System.IO命名空间内,有如下图的类用于浏览文件系统和执行操作.

这些类的作用是:
.System.MarshalByRefObject--.Net类中用于远程操作的基对象类,允许在应用程序域之间调用数据
.FileSystemInfo--表示任何文件系统对象的基类
.FileInfo和File--表示文件系统上的文件
.DirectoryInfo和Directory--表示文件系统上的文件夹
.Path--这个类包含的静态成员可以用于处理路径名
其中,Directory和File只包含静态方法,不能被实例化,适于单次操作,DirectoryInfo和FileInfo是有状态的,使用时需要实例化,适用于多次操作的场合.另外,Directory和File每次都要进行安全检查,而DirectoryInfo和FileInfo只有实例化的时候才检查.
类图如下:

好了,有了这些基本的知识,我们先来实现一个简单的方法:得到一个目录下的所有文件
首先,引入必须的命名空间:
using System;
using System.Data;
using System.IO; //文件操作类所在的命名空间
using System.Collections; //ArrayList类所在的命名空间
上面的代码用到了一个方法IsDirectory,它只是Directory.Exists()方法的封装,用起来直观一点而已:
下面再来增加一点点内容,要求返回以指定后缀名指定大小的文件列表:
有了上面的基础,再来实现一个可排序文件名的方法,这下用到了System.Data命名空间的DataTable等,本来原先自已写了一个快速排序和一个冒泡排序,可与DataView的排序速度差了一个数量级,有人说Windows程序员永远不要认为自已做得比M$好,这话说得不错.
废话少说,下面先来初始化一个DataTable,构造一个两列的DataTable,一个是文件名,另一个最后修改时间:
搜索指定目录,填充DataTable,实现方法和上面的SearchFile方法差不多,只不过是用DataTable代替ArrayList而已:
接着定义一个枚举类型,用以方便调用者:
有了上面的方法,接下来就是按条件排序了:
DataView排序速度挺快的,在我HP4016(1.73G,60G,1024M)上搜索我的音乐目录,按最后修改时间找到20000多个音乐及歌词文件费时11036毫秒,主要是IO的时间吧,第二次搜索排序2700左右.不过内存占用就没法说了..Net打开一个简单的WinForm就10多M了,示例程序在搜索排序期间最多时占内存38M多了.我们的服务器有4G内存,反正从没看到超过2G过.
呵呵..刚学C#,水平有限,本不想放到首页来的,但周围找不到同好,更没有高手指点,所以进展有限,所以想让更多的人看到,帮我指点迷津,谢了.
完整代码和示例可以从这里下载:
在我的应用中,有这样一个案例,测试机为每个已测试的PCB板或者成品生成一份测试报表,为了得知它们的测试详细情况,需要实时捕获各测试条目的结果,所以在服务器上有一Parser分析这些文件,更新到数据库.当然也可以写一个dll给测试程序去调用及更新数据库,但测试机由不同的供应商提供,很难做到这点.
已知测试机有许多,有许多不同的测试工位,同一时间会产生大量的文件,相临工位有流程控制要求,即上一工位没有Pass记录,下一工位不允许通过.所以Parser要按时间的先后顺序来处理测试报表.查询MSDN帮助文档,并没有实现这个要求相关的类,所以利用DataView的SoryBy方法来实现它,实现的道理很简单,从源目录得到文件名及属性,构造一个DataTable,一列为文件名,另一列为最后修改时间,这样就可以用SortBy方法来处理它了,呵呵,有点取巧.
下面先来了解下C#中操作文件系统的类:
.Net中,有关文件系统操作的类都在System.IO命名空间内,有如下图的类用于浏览文件系统和执行操作.

这些类的作用是:
.System.MarshalByRefObject--.Net类中用于远程操作的基对象类,允许在应用程序域之间调用数据
.FileSystemInfo--表示任何文件系统对象的基类
.FileInfo和File--表示文件系统上的文件
.DirectoryInfo和Directory--表示文件系统上的文件夹
.Path--这个类包含的静态成员可以用于处理路径名
其中,Directory和File只包含静态方法,不能被实例化,适于单次操作,DirectoryInfo和FileInfo是有状态的,使用时需要实例化,适用于多次操作的场合.另外,Directory和File每次都要进行安全检查,而DirectoryInfo和FileInfo只有实例化的时候才检查.
类图如下:

好了,有了这些基本的知识,我们先来实现一个简单的方法:得到一个目录下的所有文件
首先,引入必须的命名空间:
using System;
using System.Data;
using System.IO; //文件操作类所在的命名空间
using System.Collections; //ArrayList类所在的命名空间
1 /// <summary>
2 /// 返回指定目录(包括子目录)的所有的文件列表,找到的文件是系统随机的
3 /// </summary>
4 /// <param name="path">有效的目录</param>
5 /// <returns>ArrayList类型</returns>
6 public ArrayList SearchFile(string path){
7 if (!IsDirectory(path)){
8 return null;
9 }
10 DirectoryInfo dir = new DirectoryInfo(path);
11 FileSystemInfo[] entryies = dir.GetFileSystemInfos();
12 FileSystemInfo file;
13
14 for (int i=0;i<entryies.Length;i++){
15 file=entryies[i];
16 //如果是一个文件
17 if(file.GetType()==typeof(FileInfo)){
18 fileList.Add(file.FullName);
19 }
20 //如果是一个目录
21 else if(file.GetType()==typeof(DirectoryInfo)){
22 SearchFile(file.FullName);
23 }
24 }
25 return fileList;
26 }
2 /// 返回指定目录(包括子目录)的所有的文件列表,找到的文件是系统随机的
3 /// </summary>
4 /// <param name="path">有效的目录</param>
5 /// <returns>ArrayList类型</returns>
6 public ArrayList SearchFile(string path){
7 if (!IsDirectory(path)){
8 return null;
9 }
10 DirectoryInfo dir = new DirectoryInfo(path);
11 FileSystemInfo[] entryies = dir.GetFileSystemInfos();
12 FileSystemInfo file;
13
14 for (int i=0;i<entryies.Length;i++){
15 file=entryies[i];
16 //如果是一个文件
17 if(file.GetType()==typeof(FileInfo)){
18 fileList.Add(file.FullName);
19 }
20 //如果是一个目录
21 else if(file.GetType()==typeof(DirectoryInfo)){
22 SearchFile(file.FullName);
23 }
24 }
25 return fileList;
26 }
上面的代码用到了一个方法IsDirectory,它只是Directory.Exists()方法的封装,用起来直观一点而已:
1 /// <summary>
2 /// 检查指定的路径是不是一个有效的目录
3 /// </summary>
4 /// <param name="dirPath">需要判断的路径名</param>
5 /// <returns>是有效的目录,返回true</returns>
6 public static bool IsDirectory(string dirPath) {
7 if (!Directory.Exists(dirPath)) {
8 return false;
9 }
10 return true;
11 }
2 /// 检查指定的路径是不是一个有效的目录
3 /// </summary>
4 /// <param name="dirPath">需要判断的路径名</param>
5 /// <returns>是有效的目录,返回true</returns>
6 public static bool IsDirectory(string dirPath) {
7 if (!Directory.Exists(dirPath)) {
8 return false;
9 }
10 return true;
11 }
下面再来增加一点点内容,要求返回以指定后缀名指定大小的文件列表:
1 /// <summary>
2 /// 返回指定目录(包括子目录)的指定数目的文件,未排序,系统随机生成
3 /// </summary>
4 /// <param name="path">有效的目录</param>
5 /// <param name="filter">文件类型(*.*,mp3,txt等),多个格式以|号分隔</param>
6 /// <param name="top">返回的文件个数,这个参数为负,返回所有找到的文件</param>
7 public ArrayList SearchFile(string path,string filter,int top){
8 if (!IsDirectory(path)){
9 return null;
10 }
11
12 DirectoryInfo dir = new DirectoryInfo(path);
13 //指定目录下的文件或者文件夹
14 FileSystemInfo[] entryies = dir.GetFileSystemInfos();
15 FileSystemInfo file;
16
17 for (int i=0;i<entryies.Length;i++){
18 //top为负,返回所有找到的文件
19 if (top>0){
20 //已达指定数目的文件,则返回
21 if (fileList.Count>=top){
22 return fileList;
23 }
24 }
25 file=entryies[i];
26 //如果是一个文件
27 if(file.GetType()==typeof(FileInfo)){
28 //*.*返回所有文件
29 if (filter=="*.*")
30 fileList.Add(file.FullName);
31 else{
32 //得到文件过滤器数组
33 string[] filters = filter.Split(new char[]{'|'});
34 foreach(string flt in filters){
35 if (file.Name.ToLower().EndsWith(flt.ToLower()))
36 fileList.Add(file.FullName);
37 }
38 }
39 }
40 //如果是一个目录
41 else if(file.GetType()==typeof(DirectoryInfo)){
42 //递归搜索
43 SearchFile(file.FullName,filter,top);
44 }
45 }
46 return fileList;
47 }
2 /// 返回指定目录(包括子目录)的指定数目的文件,未排序,系统随机生成
3 /// </summary>
4 /// <param name="path">有效的目录</param>
5 /// <param name="filter">文件类型(*.*,mp3,txt等),多个格式以|号分隔</param>
6 /// <param name="top">返回的文件个数,这个参数为负,返回所有找到的文件</param>
7 public ArrayList SearchFile(string path,string filter,int top){
8 if (!IsDirectory(path)){
9 return null;
10 }
11
12 DirectoryInfo dir = new DirectoryInfo(path);
13 //指定目录下的文件或者文件夹
14 FileSystemInfo[] entryies = dir.GetFileSystemInfos();
15 FileSystemInfo file;
16
17 for (int i=0;i<entryies.Length;i++){
18 //top为负,返回所有找到的文件
19 if (top>0){
20 //已达指定数目的文件,则返回
21 if (fileList.Count>=top){
22 return fileList;
23 }
24 }
25 file=entryies[i];
26 //如果是一个文件
27 if(file.GetType()==typeof(FileInfo)){
28 //*.*返回所有文件
29 if (filter=="*.*")
30 fileList.Add(file.FullName);
31 else{
32 //得到文件过滤器数组
33 string[] filters = filter.Split(new char[]{'|'});
34 foreach(string flt in filters){
35 if (file.Name.ToLower().EndsWith(flt.ToLower()))
36 fileList.Add(file.FullName);
37 }
38 }
39 }
40 //如果是一个目录
41 else if(file.GetType()==typeof(DirectoryInfo)){
42 //递归搜索
43 SearchFile(file.FullName,filter,top);
44 }
45 }
46 return fileList;
47 }
有了上面的基础,再来实现一个可排序文件名的方法,这下用到了System.Data命名空间的DataTable等,本来原先自已写了一个快速排序和一个冒泡排序,可与DataView的排序速度差了一个数量级,有人说Windows程序员永远不要认为自已做得比M$好,这话说得不错.

1 /// <summary>
2 /// 初始化
3 /// </summary>
4 private void Init(){
5 _dt = new DataTable("FilesTable");
6 _dt.Columns.Add("FileName", typeof(string));
7 _dt.Columns.Add("LastUpdated", typeof(DateTime));
8 _ds = new DataSet("FileList");
9 _ds.Tables.Add(_dt);
10 _dv = new DataView(_ds.Tables[0]);
11 fileList= new ArrayList();
12 }
2 /// 初始化

3 /// </summary>
4 private void Init(){
5 _dt = new DataTable("FilesTable");
6 _dt.Columns.Add("FileName", typeof(string));
7 _dt.Columns.Add("LastUpdated", typeof(DateTime));
8 _ds = new DataSet("FileList");
9 _ds.Tables.Add(_dt);
10 _dv = new DataView(_ds.Tables[0]);
11 fileList= new ArrayList();
12 }
搜索指定目录,填充DataTable,实现方法和上面的SearchFile方法差不多,只不过是用DataTable代替ArrayList而已:
1 /// <summary>
2 /// 得到指定目录下所有指定文件格式的文件,包括子目录
3 /// </summary>
4 /// <param name="path">有效的目录</param>
5 /// <param name="filter">文件类型(*.*,mp3,txt等),*.*返回所有文件,
6 /// 多种格式以|字符分隔,如mp3|txt|log,不区分大小写</param>
7 private void FileScout(string path,string filter){
8 DirectoryInfo dir = new DirectoryInfo(path);
9 FileSystemInfo[] entryies = dir.GetFileSystemInfos();
10 FileSystemInfo file ;
11
12 for (int i=0;i<entryies.Length;i++){
13 file=entryies[i];
14 //如果是一个文件
15 if(file.GetType()==typeof(FileInfo)){
16 _dr = _dt.NewRow();
17 if (filter == "*.*"){
18 _dr[0] = file.FullName.ToString();
19 _dr[1] = file.LastWriteTime;
20 //把文件名和最后修改时间加入DataTable
21 _dt.Rows.Add(_dr);
22 }
23 else{
24 //得到文件过滤器数组
25 string[] filters = filter.Split(new char[]{'|'});
26 foreach(string flt in filters){
27 if(file.Name.ToLower().EndsWith(flt.ToLower())){
28 _dr[0] = file.FullName.ToString();
29 _dr[1] = file.LastWriteTime;
30 //把文件名和最后修改时间加入DataTable
31 _dt.Rows.Add(_dr);
32 }
33 }
34 }
35 }
36 //如果是一个目录,递归调用FileScout方法
37 else if(file.GetType()==typeof(DirectoryInfo)){
38 FileScout(file.FullName,filter);
39 }
40 }
41 }
42
2 /// 得到指定目录下所有指定文件格式的文件,包括子目录
3 /// </summary>
4 /// <param name="path">有效的目录</param>
5 /// <param name="filter">文件类型(*.*,mp3,txt等),*.*返回所有文件,
6 /// 多种格式以|字符分隔,如mp3|txt|log,不区分大小写</param>
7 private void FileScout(string path,string filter){
8 DirectoryInfo dir = new DirectoryInfo(path);
9 FileSystemInfo[] entryies = dir.GetFileSystemInfos();
10 FileSystemInfo file ;
11
12 for (int i=0;i<entryies.Length;i++){
13 file=entryies[i];
14 //如果是一个文件
15 if(file.GetType()==typeof(FileInfo)){
16 _dr = _dt.NewRow();
17 if (filter == "*.*"){
18 _dr[0] = file.FullName.ToString();
19 _dr[1] = file.LastWriteTime;
20 //把文件名和最后修改时间加入DataTable
21 _dt.Rows.Add(_dr);
22 }
23 else{
24 //得到文件过滤器数组
25 string[] filters = filter.Split(new char[]{'|'});
26 foreach(string flt in filters){
27 if(file.Name.ToLower().EndsWith(flt.ToLower())){
28 _dr[0] = file.FullName.ToString();
29 _dr[1] = file.LastWriteTime;
30 //把文件名和最后修改时间加入DataTable
31 _dt.Rows.Add(_dr);
32 }
33 }
34 }
35 }
36 //如果是一个目录,递归调用FileScout方法
37 else if(file.GetType()==typeof(DirectoryInfo)){
38 FileScout(file.FullName,filter);
39 }
40 }
41 }
42
接着定义一个枚举类型,用以方便调用者:
1 /// <summary>
2 /// 排序类型,按指定的条件排序
3 /// </summary>
4 public enum SortBy{
5 /// <summary>
6 /// 按文件名升序
7 /// </summary>
8 FileNameASC,
9 /// <summary>
10 /// 按文件名降序
11 /// </summary>
12 FileNameDESC,
13 /// <summary>
14 /// 按最后修改时间升序
15 /// </summary>
16 LastUpdatedASC,
17 /// <summary>
18 /// 按最后修改时间降序
19 /// </summary>
20 LastUpdatedDESC
21 }
2 /// 排序类型,按指定的条件排序
3 /// </summary>
4 public enum SortBy{
5 /// <summary>
6 /// 按文件名升序
7 /// </summary>
8 FileNameASC,
9 /// <summary>
10 /// 按文件名降序
11 /// </summary>
12 FileNameDESC,
13 /// <summary>
14 /// 按最后修改时间升序
15 /// </summary>
16 LastUpdatedASC,
17 /// <summary>
18 /// 按最后修改时间降序
19 /// </summary>
20 LastUpdatedDESC
21 }
有了上面的方法,接下来就是按条件排序了:
1 /// <summary>
2 /// 返回一个指定目录指定大小已排序的文件列表,如果top参数小于0,返回所有文件
3 /// </summary>
4 /// <param name="path">有效的目录名</param>
5 /// <param name="filter">文件类型(*.*,mp3,txt等),*.*返回所有文件,
6 /// 多种格式以|字符分隔,如mp3|txt|log,不区分大小写</param>
7 /// <param name="sortBy">按条件排序,文件名升降序,修改时间升降序</param>
8 /// <param name="top" >返回指定记录大小</param>
9 public ArrayList FileSorter(string path,string filter,FileSearcher.SortBy sortBy,int top){
10 if (!IsDirectory(path)){
11 return null;
12 }
13
14 //搜索指定目录下的所有文件
15 FileScout(path,filter);
16
17 if (_dt!=null){
18 switch (sortBy){
19 case SortBy.FileNameASC:
20 _dv.Sort = "FileName ASC";
21 break;
22 case SortBy.FileNameDESC:
23 _dv.Sort = "FileName DESC";
24 break;
25 case SortBy.LastUpdatedASC:
26 _dv.Sort = "LastUpdated ASC";
27 break;
28 case SortBy.LastUpdatedDESC:
29 _dv.Sort = "LastUpdated DESC";
30 break;
31 default:
32 _dv.Sort = "LastUpdated ASC";
33 break;
34 }
35 for(int i=0;i<_dv.Count;i++){
36 //top为负数,返回所有的文件
37 if(top>0){
38 if (fileList.Count < top)
39 fileList.Add(_dv[i]["FileName"].ToString());
40 else
41 return fileList; //已找到指定数量的文件
42 }
43 else
44 fileList.Add(_dv[i]["FileName"].ToString());
45 }
46 }
47 else {
48 return null;
49 }
50 return fileList;
51 }
2 /// 返回一个指定目录指定大小已排序的文件列表,如果top参数小于0,返回所有文件
3 /// </summary>
4 /// <param name="path">有效的目录名</param>
5 /// <param name="filter">文件类型(*.*,mp3,txt等),*.*返回所有文件,
6 /// 多种格式以|字符分隔,如mp3|txt|log,不区分大小写</param>
7 /// <param name="sortBy">按条件排序,文件名升降序,修改时间升降序</param>
8 /// <param name="top" >返回指定记录大小</param>
9 public ArrayList FileSorter(string path,string filter,FileSearcher.SortBy sortBy,int top){
10 if (!IsDirectory(path)){
11 return null;
12 }
13
14 //搜索指定目录下的所有文件
15 FileScout(path,filter);
16
17 if (_dt!=null){
18 switch (sortBy){
19 case SortBy.FileNameASC:
20 _dv.Sort = "FileName ASC";
21 break;
22 case SortBy.FileNameDESC:
23 _dv.Sort = "FileName DESC";
24 break;
25 case SortBy.LastUpdatedASC:
26 _dv.Sort = "LastUpdated ASC";
27 break;
28 case SortBy.LastUpdatedDESC:
29 _dv.Sort = "LastUpdated DESC";
30 break;
31 default:
32 _dv.Sort = "LastUpdated ASC";
33 break;
34 }
35 for(int i=0;i<_dv.Count;i++){
36 //top为负数,返回所有的文件
37 if(top>0){
38 if (fileList.Count < top)
39 fileList.Add(_dv[i]["FileName"].ToString());
40 else
41 return fileList; //已找到指定数量的文件
42 }
43 else
44 fileList.Add(_dv[i]["FileName"].ToString());
45 }
46 }
47 else {
48 return null;
49 }
50 return fileList;
51 }
DataView排序速度挺快的,在我HP4016(1.73G,60G,1024M)上搜索我的音乐目录,按最后修改时间找到20000多个音乐及歌词文件费时11036毫秒,主要是IO的时间吧,第二次搜索排序2700左右.不过内存占用就没法说了..Net打开一个简单的WinForm就10多M了,示例程序在搜索排序期间最多时占内存38M多了.我们的服务器有4G内存,反正从没看到超过2G过.
呵呵..刚学C#,水平有限,本不想放到首页来的,但周围找不到同好,更没有高手指点,所以进展有限,所以想让更多的人看到,帮我指点迷津,谢了.
完整代码和示例可以从这里下载: