C#文件系统

本文讨论Windows操作系统中驱动器、文件、文件夹(目录)和路径的处理,并将常用操作封装到cfx命名空间的Drv、CFile和Dir类。

路径

.Net Framework类库中,文件系统操作资源定义在System.IO命名空间。路径处理资源主要包括Path类,下面是一些常用方法:

  • GetDirectoryName(path)方法,返回路径中的目录部分。
  • GetFileName(path)方法,返回路径中的文件名。
  • GetFileNameWithoutExtension(path)方法,返回路径中的基本文件名,不包含扩展名。
  • HasExtension(path)方法,判断路径中是否包含扩展名,包含时返回true,否则返回false。
  • GetExtension(path)方法,返回路径中的扩展名部分,包括最后一个圆点(.)及其后面的内容。
  • ChangeExtension(path, extName)方法,改变路径中的扩展名。
  • GetPathRoot(path)方法,返回路径中的根路径。
  • Combine(path,filename)方法,将目录路径和文件名组合成完整的路径。

下面的代码演示了这些方法的应用。

C#
using System;
using System.IO;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"d:\test\abc.txt";
            //
            Console.WriteLine(Path.GetDirectoryName(path));
            Console.WriteLine(Path.GetFileName(path));
            Console.WriteLine(Path.GetFileNameWithoutExtension(path));
            Console.WriteLine(Path.HasExtension(path)); 
            Console.WriteLine(Path.GetExtension(path));
            Console.WriteLine(Path.ChangeExtension(path, ".text"));
            Console.WriteLine(Path.GetPathRoot(path));
            Console.WriteLine(Path.Combine(@"d:\tmp", @"xyz.txt"));
        }
    }
}

代码执行结果如下图所示。

Path类常用方法

应注意,使用Path类操作时并不检查路径中的目录和文件是否真实存在,只是对路径的文本内容进行操作。

文件夹(目录)

Directory类定义了目录操作的静态方法,常用的有:

  • Exists()方法,判断路径中的目录是否存在,存在时返回true,否则返回false。
  • CreateDirectory()方法,创建目录,操作成功时返回目录的DirectoryInfo对象。
  • Delete()方法,删除目录。参数一指定目录的路径;参数二指定是否删除子目录和文件,默认为false。
  • GetDirectories()方法,返回目录中的子目录集合,返回类型为string[]数组。参数二可以指定过滤条件,其中可以使用?和*通配符,其中,?匹配一个字符,*匹配零个或多个字符。
  • GetFiles()方法,返回目录中的文件名集合,文件名包含完整路径,返回类型为string[]数组。参数二同样可以指定过滤条件,如"*.txt"表示所有.txt扩展名的文本文件。
  • GetParent()方法,返回父目录对象,类型为DirectoryInfo。
  • Move()方法,移动目录,参数一指定目录原路径,参数二指定新的路径。Move()方法也可以用于目录的更名操作。

DirectoryInfo类是目录处理的可实例化类,构造函数中可以使用包含目录路径的字符串作为参数,创建对象后可以使用以下属性获取目录信息:

  • Exists,目录是否存在。
  • FullName,目录包含完整路径的名称。
  • Name,目录名称。
  • Parent,父目录的DirectoryInfo对象。
  • Root,所在驱动器根目录的DirectoryInfo对象。

DirectoryInfo对象的常用方法包括:

  • Create()方法,创建目录。此方法无返回值。
  • CreateSubdirectory()方法,创建子目录,参数指定子目录名称,创建成功后返回子目录的DirectoryInfo对象。
  • Delete()方法,删除目录,参数可以指定是否删除子目录和文件,默认为false。
  • GetDirectories()方法,返回子目录集合,返回类型为DirectoryInfo[]数组。
  • GetFiles()方法,返回文件集合,返回类型为FileInfo[]数组。
  • MoveTo()方法,移动目录。

Directory和DirectoryInfo类中有一些同名和功能相似的操作方法,需要注意这些方法的参数和返回值,并根据需要合理使用。此外,还可以对常用的功能进行封装,如下面的代码(cfx/Dir.cs)。

C#
using System.IO;
using System.Collections.Generic;
//
namespace cfx
{
    public static class Dir
    {
        // 创建路径
        public static bool Create(string path)
        {
            try
            {
                if (Directory.Exists(path))
                {
                    return true;
                }
                else
                {
                    Directory.CreateDirectory(path);
                    return true;
                }
            }
            catch
            {
                return false;
            }
        }

        // 判断目录是否存在
        public static bool Exists(string path)
        {
            return Directory.Exists(path);
        }

        // 按过滤读取文件名列表
        public static string[] GetFiles(string path, 
            string filter = "*.*", bool onlyFileName = false)
        {
            string[] files = Directory.GetFiles(path, filter);
            if (onlyFileName)
            {
                List<string> result = new List<string>();
                for (int i = 0; i < files.Length; i++)
                    result.Add(Path.GetFileName(files[i]));
                return result.ToArray();
            }
            else
            {
                return files;
            }
        }
    }
}

代码中定义了Dir静态类,其中包含三个方法:

  • Create(string path),创建path中指定的目录,成功返回true,失败返回false,如果目录已存在同样返回true。
  • Exists(string path),判断目录是否存在,存在时返回true,否则返回false。
  • GetFiles(string path, string filter = "*.*", bool onlyFileName = false),返回目录中的文件列表;参数path指定目录的路径;参数filter指定文件名的过滤,默认为所有文件;参数onlyFileName指定是否只返回文件名,默认为false,返回包含路径的文件名。

假设不存在d:\test1目录,有d:\test目录,其中包含a.txt、b.txt和c.txt三个文件;下面的代码测试了cfx.Dir类的使用。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"d:\test\";
            string path1 = @"d:\test\1";
            //
            Console.WriteLine(Dir.Exists(path1));  // False
            Console.WriteLine(Dir.Create(path1));  // True
            Console.WriteLine(Dir.Create(path1));  // False
            //
            string[] fileNames = Dir.GetFiles(path, "*.txt", true);
            foreach(string s in fileNames)
            {
                Console.WriteLine(s);
            }
        }
    }
}

代码执行结果如下图所示。

测试Dir类

文件

文件同样有两个类,分别是定义静态方法的File类和可实例化的FileInfo类。File类常用的方法有:

AppendLines(path,lines),在文件后追加多行文本,如果文件不存在则创建它。

AppendText(path,text),在文件后追加文本内容,如果文件不存在则创建它。

Copy(path1,path2),将path1文件复制到path2,可以使用第三个参数指定在file2存在时是否覆盖它,不指定或指定为false,则path2已存在时会产生异常。

Exists(path),判断文件是否存在,存在时返回true,否则返回false。

Move(path1,path2),将path1文件移动到path2位置。

ReadAllLines(path),读取文本文件的所有行,返回string[]数组。

ReasAllText(path),读取文本文件的所有内容,返回string类型。

WriteAllLines(path, lines),将多行内容写入文本文件,操作将覆盖文件中的原有内容,追加文本行时应使用AppendLines()方法。

WriteAllText(path, text),将文本内容写入文本文件,操作将覆盖文件中的原有内容,追加文本内容时应使用AppendText()方法。

FileInfo类的构造函数可以使用包含文件位置的路径作为参数,常用属性包括:

  • Directory,返回文件所在目录的DirectoryInfo对象。
  • DirectoryName,返回所在目录的路径。
  • Exists,文件是否存在。
  • IsReadOnly,是否为只读文件。
  • Length,文件的字节数。
  • Name,文件名。

FileInfo对象方法和File类的方法功能相似,这里不再一一列举。

下面的代码(cfx/CFile.cs),可以对文件的常用操作进行封装。

C#
using System.Drawing.Imaging;
using System.Drawing;
using System.IO;
//
namespace cfx
{
    public static class CFile
    {
        // 文件是否存在
        public static bool Exists(string filename)
        {
            try { return File.Exists(filename); }
            catch { return false; }
        }

        // 返回路径中的文件名
        public static string GetFileName(string path)
        {
            try { return Path.GetFileName(path); }
            catch { return ""; }
        }

        // 不包含扩展名的文件名
        public static string GetBaseName(string path)
        {
            try { return Path.GetFileNameWithoutExtension(path); }
            catch { return ""; }
        }

        // 文件扩展名(小写)
        public static string GetExtName(string path)
        {
            try { return Path.GetExtension(path).ToLower(); }
            catch { return ""; }
        }

        // 删除文件
        public static bool Delete(string path)
        {
            try
            {
                if (File.Exists(path))
                {
                    File.Delete(path);
                    return true;
                }
                else { return true; }
            }
            catch
            {
                return false;
            }
        }

        // 复制文件
        public static bool Copy(string source, string target)
        {
            try
            {
                File.Copy(source, target, true);
                return File.Exists(target);
            }
            catch { return false; }
        }

        //
        public static string ReadAllText(string filename)
        {
            try { return File.ReadAllText(filename); }
            catch { return ""; }
        }

        //
        public static bool WriteAllText(string filename, string content)
        {
            try
            {
                File.WriteAllText(filename, content);
                return File.Exists(filename);
            }
            catch { return false; }
        }

        // 读取所有行
        public static string[] ReadAllLines(string path)
        {
            try { return File.ReadAllLines(path); }
            catch { return new string[0]; }
        }

        // 写入所有行
        public static bool WriteLines(string filename, string[] lines)
        {
            try
            {
                File.WriteAllLines(filename, lines);
                return File.Exists(filename);
            }
            catch { return false; }
        }

        // 文件重命名,相同目录下
        public static bool Rename(string path, string newFileName)
        {
            try
            {
                string dirPath = Path.GetDirectoryName(path);
                string newPath = Path.Combine(dirPath, newFileName);
                if (Exists(path) == false || Exists(newPath) == true)
                    return false;
                File.Move(path, newPath);
                return true;
            }
            catch { return false; }
        }
        

        // 判断是否为.zip文件
        public static bool IsZip(string filename)
        {
            try { return Path.GetExtension(filename).ToLower() == ".zip"; }
            catch { return false; }
        }
    }
}

代码中封装了CFile类,其中包含了文件的一些常用操作。

Exists(string filename)方法,判断文件是否存在。

GetFileName(string path)方法,返回文件名,包括基本名称和扩展名。

GetBaseName(string path)方法,返回文件基本名称,不包含扩展名。

GetExtName(string path)方法,返回文件的扩展名。

Delete(string path)方法,删除文件。如果文件不存在也会返回true。

Copy(string source, string target)方法,复制文件。

ReadAllText(string filename)方法,读取文本文件的所有内容,出错时返回空字符串。

WriteAllText(string filename, string content)方法,将文本写入文本文件,操作会替换文件原内容,写后确认文件存在并返回true;操作错误返回false。

ReadAllLines(string path)方法,读取文本文件的所有行并返回string[]数组,读取错误返回包含0个元素的空数组。

WriteLines(string filename, string[] lines)方法,将多行文本写入文本文件,操作成功返回true,否则返回false。

Rename(string path, string newFileName)方法,文件重命名。

IsZip(string filename)方法,以扩展名是为是.zip判断文件是否为ZIP文件。

假设有d:\test目录,其中包含a.txt文件,文件中包含a1、a2、a3三行文本;下面的代码,在Program.cs文件中测试CFile类的应用。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string path1 = @"d:\test\a.txt";
            string path2 = @"d:\test\b.txt";
            //
            Console.WriteLine(CFile.Exists(path1));  // True
            Console.WriteLine(CFile.Exists(path2));  // Fasle
            Console.WriteLine(CFile.GetFileName(path1));  // a.txt
            Console.WriteLine(CFile.GetBaseName(path1));  // a
            Console.WriteLine(CFile.GetExtName(path1));  // .txt
            string[] lines = CFile.ReadAllLines(path1);
            foreach(string ln in lines)
            {
                Console.WriteLine(ln);
            }
            Console.WriteLine(CFile.WriteLines(path2, lines));  // True
        }
    }
}

代码执行结果如下图所示。

文件操作测试

驱动器

获取驱动器信息可以使用DriveInfo类,其构造函数需要使用"a"到"z"的字符串作为参数,其中,"a"和"b"为软盘驱动器,从"c"开始应用于硬盘分区或其它类型的驱动器。下面的代码,通过在cfx/Drv.cs文件中封装Drv类来演示DriveInfo类的应用。

C#
using System.IO;

namespace cfx
{
    public static class Drv
    {
        // 获取全部尺寸
        public static long GetTotalSize(string s)
        {
            try { return new DriveInfo(s).TotalSize; }
            catch { return -1000; }
        }
        // 获取可用尺寸
        public static long GetAvailableSize(string s)
        {
            try { return new DriveInfo(s).AvailableFreeSpace; }
            catch { return -1000; }
        }
        // 获取空闲尺寸
        public static long GetFreeSize(string s)
        {
            try { return new DriveInfo(s).TotalFreeSpace; }
            catch { return -1000; }
        }
        // 判断驱动器状态
        public static bool IsReady(string s)
        {
            try { return new DriveInfo(s).IsReady; }
            catch { return false; }
        }
        // 获取驱动器格式,如NTFS,FAT32
        public static string GetFormat(string s)
        { 
            try { return new DriveInfo(s).DriveFormat; }
            catch { return ""; }
        }
        // 驱动器名称,如C:\
        public static string GetName(string s)
        {
            try { return new DriveInfo(s).Name; }
            catch { return ""; }
        }
        // 获取驱动器卷标
        public static string GetVolumeLabel(string s)
        {
            try { return new DriveInfo(s).VolumeLabel; }
            catch { return ""; }
        }
        // 驱动器类型,返回整数值
        public static int GetDriveType(string s)
        {
            try { return (int)new DriveInfo(s).DriveType; }
            catch { return -1000; }
        }
    }
}

代码中定义了Drv类,其中的静态方法参数都使用"a"到"z"的字符串;定义的方法包括:

GetTotalSize()方法,返回驱动器全部尺寸,单位为字节。

GetAvailableSize()方法,返回驱动器可用的尺寸,单位为字节。

GetFreeSize()方法,返回驱动器空闲尺寸,单位为字节。

IsReady()方法,返回驱动器状态。

GetFormat()方法,返回驱动器格式,如NTFS、FAT32。

GetName()方法,返回驱动器名称,如C:\。

GetVolumeLabel()方法,返回驱动器卷标。

GetDriveType()方法,获取驱动器类型,返回为DriveType枚举成员的整数值,对应的枚举成员和整数值如下表。

名称说明
Unknown0未知类型。
NoRootDirectory1驱动器没有根目录。
Removable2可移动存储设备,如U盘。
Fixed3固定磁盘,如硬盘。
Network4网络驱动器。
CDRom5光盘设备。
Ram6RAM磁盘。

下面的代码显示了作者计算中D盘的相关信息。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "d";
            Console.WriteLine(Drv.GetTotalSize(s));
            Console.WriteLine(Drv.GetAvailableSize(s));
            Console.WriteLine(Drv.GetName(s));
            Console.WriteLine(Drv.IsReady(s));
        }
    }
}

获取驱动器的尺寸时,默认单位为字节,可以使用整数的位运算改变单位对应的数值,如下面的代码。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "d";
            Console.WriteLine(Drv.GetTotalSize(s)+" bytes");
            Console.WriteLine((Drv.GetTotalSize(s) >> 10) + " KB");
            Console.WriteLine((Drv.GetTotalSize(s) >> 20) + " MB");
            Console.WriteLine((Drv.GetTotalSize(s) >> 30) + " GB");
        }
    }
}

需要保留小数部分,可以参考如下代码。

C#
using System;
using cfx;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            string s = "d";
            Console.WriteLine(Drv.GetTotalSize(s) + " bytes");
            Console.WriteLine(Math.Round(Drv.GetTotalSize(s) / Math.Pow(2, 10),2)+" KB");
            Console.WriteLine(Math.Round(Drv.GetTotalSize(s) / Math.Pow(2, 20), 2) + " MB");
            Console.WriteLine(Math.Round(Drv.GetTotalSize(s) / Math.Pow(2, 30), 2) + " GB");
        }
    }
}

代码中,使用Math.Pow()方法计算2的次方,返回结果为double类型,然后使用尺寸数据除以它,结果同样为double类型,最后,使用Math.Round()方法保留两位小数形式并显示。

以MB为单位是常见的操作,下面的代码(cfx/Drv.cs),会在Drv类中添加新的驱动器尺寸获取方法,其返回值为整数,单位为MB。

C#
using System.IO;

namespace cfx
{
    public static class Drv
    {
        // 其它参数
        // 获取全部尺寸MB
        public static long GetTotalMB(string s)
        {
            try { return new DriveInfo(s).TotalSize>>20; }
            catch { return -1000; }
        }
        // 获取可用尺寸
        public static long GetAvailableMB(string s)
        {
            try { return new DriveInfo(s).AvailableFreeSpace>>20; }
            catch { return -1000; }
        }
        // 获取空闲尺寸
        public static long GetFreeMB(string s)
        {
            try { return new DriveInfo(s).TotalFreeSpace>>20; }
            catch { return -1000; }
        }
    }
}

实际工作中,可以根据需要扩展Dir、Drv和CFile类的方法。