C#OOP CH09 文件操作

本文档详细介绍了使用C#进行文件读写操作的步骤,包括创建文件流、读写器的使用、处理编码问题以及简化读写过程。此外,还展示了如何进行文件和文件夹操作,如创建、删除和复制文件。最后,通过一个小型资源管理器的案例,讲解了如何读取目录信息、显示文件信息以及实现文件复制和删除功能。
摘要由CSDN通过智能技术生成

C#OOP CH09 文件操作

本章目标

  • 掌握文本文件的读写
  • 会进行文件和文件夹操作

1. 文件操作

1.1 如何读写文本文件

通常来讲,用C#程序读写一个文件需要以下五个基本步骤:

  1. 创建文件流
  2. 创建阅读器或者写入器
  3. 执行读写操作
  4. 关闭阅读器或者写入器
  5. 关闭文件流

代码演示如下:

写:

string path = this.txtPath.Text.Trim();
string context = this.txtContent.Text.Trim();
//创建文件流
FileStream fs = File.Create(path);
//创建写入流
StreamWriter sw = new StreamWriter(fs);
//执行写入操作
sw.Write(content);
//关闭写入流
sw.Close();
//关闭文件流
fs.Close();

读:

string path = this.txtPath.Text.Trim();
//创建文件流
FileStream fs = File.OpenRead(path);
//创建读取流
StreamReader sr = new StreamReader(fs);
//执行读取操作
string content = sr.ReadToEnd();
this.txtContent.Text = content;
//关闭读取流
sr.Close();
//关闭文件流
fs.Close();

简化步骤如下:

  1. 创建文件流
  2. 调用方法执行读写操作
  3. 关闭文件流

简化代码演示如下:

写:

using (FileStream fs = File.Create(path))
{
    byte[] info = new UTF8Encoding(true).GetBytes(content);
    fs.Write(info, 0, info.Length);
}

读:

 using (FileStream fs = File.OpenRead(path))
 {
     byte[] b = new byte[1024];
     UTF8Encoding temp = new UTF8Encoding(true);
     StringBuilder sb = new StringBuilder();
     while (fs.Read(b, 0, b.Length) > 0)
     {
         sb.Append(temp.GetString(b));
     }
     this.txtContent.Text = sb.ToString();
 }

1.2 创建文件流

  1. 创建文件流时,需要在它的构造函数中指定参数,参数说明如下:
    在这里插入图片描述

  2. 关闭文件流:
    写入结束后一定要关闭文件流 myfs.Close()。

1.3 创建写入器和读取器

  1. 文本文件的写入器和读取器方法如图所示:
    在这里插入图片描述

  2. 解决乱码问题:

    使用StreamReader读取文件中的中文文本,有时会产生乱码问题。这并不是因为C#语言有问题,而是因为不同文件编码格式可能不同。如果在编码时给文件读取器对象指定对应的编码格式,问题就迎刃而解了。

    以下代码展示了如何使用指定的编码格式:

    StreamReader sr = new StreamReader("文件名",Encoding.Default);
    
    • 可以通过Encoding类的静态成员指定编码格式。例如:

      Encoding.UTF8:获取UTF-8格式的编码

      Encoding.Default:获取操作系统的当前编码

    • 也可以通过Encoding类的静态方法GetEncoding(string name) 指定字符编码,参数name必须是C#支持的编码名。例如:

      StreamReader sr = new StreamReader("文件名",Encoding.GetEncoding("GB2312"))
      

      常见的编码可以参考官网:https://docs.microsoft.com/zh-cn/dotnet/standard/base-types/character-encoding

  3. 使用写入器和读取器简化读写操作如下:

    写:

     using (StreamWriter sw = new StreamWriter(path,false,Encoding.UTF8))
     {
         sw.Write(this.txtContent.Text);
     }
    

    读:

    using (StreamReader sr = new StreamReader(path,Encoding.UTF8))
    {
        StringBuilder sb = new StringBuilder();
        while(sr.Peek() > -1)
        {
            sb.AppendLine(sr.ReadLine());
        }
        this.txtContent.Text = sb.ToString();
    }
    

1.4 网络电视精灵读写案例演示

需求:

将定制的频道信息写入文本文件

实现思路:

  1. 编写SaveAsText()方法,实现将定制的频道信息存入.txt文件
  2. 编写主窗体的FormClosed的事件,调用SaveAsText()方法

修改ChannelBase.cs,增加SaveAsText()方法,代码如下:

/// <summary>
///  频道管理类
/// </summary>
public class ChannelManager
{
   	//省略其他代码

    private string file = @"files/save.txt";

    private List<ChannelBase> list = new List<ChannelBase>();
    public List<ChannelBase> ChannelBaseList => list;
    public void SaveAsText()
    {
        try
        {
           using (StreamWriter writer = new StreamWriter(file,false,Encoding.UTF8))
            {
                list.ForEach(c => 
                             writer.WriteLine(c.ChannelType + "|" 
                                              + c.ChannelName + "|"  + c.Path));
            } 
        }
        catch(Exception e)
        {
            Console.WriteLine("写入文件异常,异常是:" + e.ToString());
        }            
    }
}

编写主窗体的FormClosed的事件,调用SaveAsText()方法,关键代码如下:

private void FrmTV_FormClosed(object sender, FormClosedEventArgs e)
{
    //先清空频道集合中的数据
    channelManager.ChannelBaseList.Clear();
    //判断我电视台中是否有子节点
    if(this.tvChannel.Nodes[0].Nodes.Count > 0)
    {
        foreach (TreeNode node in tvChannel.Nodes[0].Nodes)
        {
            //将频道加入到集合中
            ChannelBase channel = node.Tag as ChannelBase;
            channelManager.ChannelBaseList.Add(channel);
        }
    }
    //写入文件
    channelManager.SaveText();
    //MessageBox.Show("写入成功");
    Application.Exit();
}

需求:

读取定制频道信息

实现思路:

  • 从文本文件save.txt 读取定制的频道信息,并在主窗体运行时加载,最终实现定制频道信息的读取

修改ChannelManager.cs,增加ReadText方法,代码如下:

//读取文件
public void ReadText()
{
    try
    {
        if (!File.Exists(fileName)) return;//若文件不存在 则不执行读取操作
        using (StreamReader sr = new StreamReader(fileName,Encoding.UTF8))
        {
            while (sr.Peek() > -1)
            {
                string[] paramValue = sr.ReadLine().Split('|');
                ChannelBase channel = CreateChannelBase(paramValue[0],paramValue[1],paramValue[2]);
                ChannelBaseList.Add(channel);
            }
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
    }
}

修改主窗体的窗体加载事件,代码如下:

public partial class FrmTV : Form
{
    ChannelManager channelManager = new ChannelManager();
    private void FrmTV_Load(object sender, EventArgs e)
    {
        //读取文件
        channelManager.ReadText();
        TreeNode rootNode = new TreeNode("我的电视台",0,1);
        this.tvChannels.Nodes.Add(rootNode);
        rootNode = new TreeNode("所有电视台",0,1);
        foreach (ChannelBase item in channelManager.FullChannels.Values)
        {
            TreeNode node = new TreeNode(item.ChannelName,0,1);
            node.Tag = item;
            node.Name = item.ChannleName;
            rootNode.Nodes.Add(node);
        }
        this.tvChannels.Nodes.Add(rootNode);
        //我的电视台
        if(channelManager.ChannelBaseList.Count > 0)
        {
            foreach (ChannelBase item in channelManager.ChannelBaseList)
            {
                TreeNode node = new TreeNode(item.ChannelName, 0, 1);
                node.Tag = item;
                node.Name = item.ChannleName;
                this.tvChannels.Nodes[0].Nodes.Add(node);
            }
        }
        this.tvChannels.ExpandAll();
    }
    //省略其他代码
}

2. 文件和目录操作

2.1 File 类

在这里插入图片描述

2.2 Directory 类

在这里插入图片描述

2.3 静态类与非静态类

在这里插入图片描述

2.4 静态成员与实例成员

在这里插入图片描述

2.4 FileInfo 类

在这里插入图片描述

2.5 DirectoryInfo 类

在这里插入图片描述

3. 文件的综合运用

开发小型资源管理器,可以浏览文件信息
在这里插入图片描述

3.1 读取目录下的子目录信息

先来完成第一个功能。操作目可以Directory类和DirectoryInfo类。由于资源管理器中的目录需要多次使用,所以优先选择DirectoryInfo类。如下示例所示:使用DirectoryInfo类获取D盘下的所有目录,并将目录信息显示在TreeView树状菜单中。

窗体类中,编写代码如下:

//窗体加载
private void FrmFile_Load(object sender, EventArgs e)
{
    //加载盘符
    LoadDrivers();
}
public void LoadDrivers()
{
    string path = @"D:\";
    TreeNode node = new TreeNode(path);
    node.Tag = path;
    this.tvDic.Nodes.Add(node);
}
//节点选中改变事件
private void tvDic_AfterSelect(object sender, TreeViewEventArgs e)
{
    //获取当前选中的节点
    TreeNode node = this.tvDic.SelectedNode;
    //填充treeview
    BindInfo(node);
}
//将目录绑定到TreeView中
public void BindInfo(TreeNode node)
{
    //创建目录对象
    DirectoryInfo directoryInfo = new DirectoryInfo(node.Tag.ToString());
    //加载子目录
    DirectoryInfo[] dirs = directoryInfo.GetDirectories();
    dirs.ForEach(dir =>
                 {
                     TreeNode temp = new TreeNode(dir.Name);
                     temp.Tag = dir.FullName;
                     node.Nodes.Add(temp);
                 });
    //foreach (DirectoryInfo dir in dirs)
    //{
    //    TreeNode temp = new TreeNode(dir.Name);
    //    temp.Tag = dir.FullName;
    //    node.Nodes.Add(temp);
    //}
}

创建MyExMethod类,扩展ForEach方法,代码如下:

public static class MyExMethods
{
    public static void ForEach<T>(this IEnumerable<T> source,Action<T> action)
    {
        foreach (var item in source)
        {
            action(item);
        }
    }
}

3.2 读取目录下的文件信息

读取文件信息的方式和读取目录信息的方式大同小异。为了表示方便,我们定义一个MyFile类来存储文件信息。MyFile类的属性如下表所示:

属性类型说明
FileLengthfloat文件长度,以KB为单位
FileNamestring文件名
FilePathstring文件路径
FileTypestring文件类型

创建MyFile类,封装文件的各种信息,代码如下:

public class MyFile
{
    public string FileName { get; set; }//文件名
    private float fileLength;
    public float FileLength//文件大小
    {
        get
        {
            return fileLength;
        }
        set
        {
            fileLength = (float)Math.Round(value / 1024, 2);
        }
    }
    public string FileType { get; set; }//文件类型
    public string FilePath { get; set; }//文件路径
    public MyFile(string fileName, float fileLength, string fileType, string filePath)
    {
        this.FileName = fileName;
        this.FileLength = fileLength;
        this.FileType = fileType;
        this.FilePath = filePath;
    }
}

单击树状菜单上的目录节点,绑定该目录下文件的基本信息。代码如下:

//将目录绑定到TreeView中 并且加载目录下的子文件
public void BindInfo(TreeNode node)
{
    //创建目录对象
    DirectoryInfo directoryInfo = new DirectoryInfo(node.Tag.ToString());
    //加载子目录
    DirectoryInfo[] dirs = directoryInfo.GetDirectories();
    dirs.ForEach(dir =>
                 {
                     TreeNode temp = new TreeNode(dir.Name);
                     temp.Tag = dir.FullName;
                     node.Nodes.Add(temp);
                 });
    //foreach (DirectoryInfo dir in dirs)
    //{
    //    TreeNode temp = new TreeNode(dir.Name);
    //    temp.Tag = dir.FullName;
    //    node.Nodes.Add(temp);
    //}
    //加载子文件
    FileInfo[] fileInfo =  directoryInfo.GetFiles();
    List<MyFile> files = new List<MyFile>();
    fileInfo.ForEach(file =>files.Add(
        new MyFile(file.Name, file.Length, file.Extension, file.FullName)));
    //foreach (FileInfo file in fileInfo)
    //{
    //    MyFile myFile = new MyFile();
    //    myFile.FileName = file.Name;
    //    myFile.FileLength = file.Length;
    //    myFile.FileType = file.Extension;
    //    myFile.FilePath = file.FullName;
    //    files.Add(myFile);
    //}
    //绑定ListView
    BindListView(files);
}
//绑定ListView
public void BindListView(List<MyFile> files)
{
    this.lvFile.Items.Clear();
    files.ForEach(item =>
                  {
                      ListViewItem listViewItem = new ListViewItem(item.FileName);
                      listViewItem.SubItems.Add(item.FileLength.ToString());
                      listViewItem.SubItems.Add(item.FileType);
                      listViewItem.SubItems.Add(item.FilePath);
                      this.lvFile.Items.Add(listViewItem);
                  });
    //foreach (MyFile item in files)
    //{
    //    ListViewItem listViewItem = new ListViewItem(item.FileName);
    //    listViewItem.SubItems.Add(item.FileLength.ToString());
    //    listViewItem.SubItems.Add(item.FileType);
    //    listViewItem.SubItems.Add(item.FilePath);
    //    this.lvFile.Items.Add(listViewItem);
    //}
}

3.3 实现文件复制

需求说明

  • 完善小型资源管理器,实现文件复制
  • 支持用户从“浏览文件夹”对话框选定目标位置
    在这里插入图片描述

实现思路

  • 打开“浏览文件夹”,并获得选择的存储路径,实现代码如下
//复制
private void tsmiCopy_Click(object sender, EventArgs e)
{
    if (this.lvFile.SelectedItems.Count == 0) return;
    FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
    DialogResult result = folderBrowserDialog.ShowDialog();
    if (result == DialogResult.No) return;
    File.Copy(this.lvFile.SelectedItems[0].SubItems[3].Text,
              folderBrowserDialog.SelectedPath + "\\" + this.lvFile.SelectedItems[0].Text);
    MessageBox.Show("复制成功");
}

3.4 实现文件删除

需求说明

  • 完善小型资源管理器,实现文件删除,并刷新ListView显示
    在这里插入图片描述
    实现思路

  • 可以使用File类的Delete(string filePath)方法,实现代码如下:

//删除
private void tsmiDel_Click(object sender, EventArgs e)
{
    if (this.lvFile.SelectedItems.Count == 0) return;
    DialogResult result = MessageBox.Show("确定要删除该文件吗?", "提示", MessageBoxButtons.YesNo);
    if (result == DialogResult.No) return;
    File.Delete(this.lvFile.SelectedItems[0].SubItems[3].Text);
    MessageBox.Show("删除成功");
    //刷新
    this.lvFile.SelectedItems[0].Remove();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值