c#学习笔记-------------------------c#的IO操作

学习笔记 / 2023-08-14 / 原文

一、什么是I/O

I/O 的全称是input/output,翻译过来就是输入/输出。对于一个系统或者计算机来说,键盘、U盘、网络接口、显示器、音响、摄像头等都是IO设备。

对于c#程序来说,I/O就是与外界进行数据交换的方式。程序需要对数据进行运算,I/O就是提供数据来源和输出数据的部分。

在C# 中,I/O体系整体分为三个部分,后台存储流、装饰器流、流适配器,具体划分如下图所示:

 C#与Java不同,不区分字符流、字节流,采用Stream方式,针对Stream提供Reader与Writer的相关操作。

二、常用的c#I/O对象

1.Stream 基类

System.IO.Stream 是一个抽象类,提供了将字节(读,写等)传输到源的标准方法。

就像包装器类一样传输字节。需要从特定源读取/写入字节的类必须实现Stream类。

以下类继承Stream类

  • FileStream:从物理文件读取字节或向物理文件写入字节,无论它是.txt,.exe,.jpg还是任何其他文件。FileStream派生自Stream类。
  • MemoryStream: MemoryStream读取或写入存储在内存中的字节。
  • BufferedStream: BufferedStream从其他Stream读取或写入字节,以提高某些I / O操作的性能。
  • NetworkStream: NetworkStream从网络套接字读取或写入字节。
  • PipeStream: PipeStream读取或写入来自不同进程的字节。
  • CryptoStream: CryptoStream用于将数据流链接到密码转换。

使用方法:

FileStream:

FileStream类为文件操作提供了一个流。它可以用于执行同步和异步的读写操作。
FileStream类的帮助下,我们可以轻松地将数据读写到文件中。

写入:

using System;  
using System.IO;  
public class FileStreamExample  
{  
    public static void Main(string[] args)  
    {  
        FileStream f = new FileStream("e:\\filestream-demo.txt", FileMode.OpenOrCreate);//creating file stream  
        f.WriteByte(65);//writing byte into stream  
        f.Close();//closing stream  
    }  
}
using System;  
using System.IO;  
public class FileStreamExample  
{  
    public static void Main(string[] args)  
    {  
        FileStream f = new FileStream("e:\\filestream-demo.txt", FileMode.OpenOrCreate);  
        for (int i = 65; i <= 90; i++)  
        {  
            f.WriteByte((byte)i);  
        }  
        f.Close();  
    }  
}
using System;  
using System.IO;  
public class FileStreamExample  
{  
    public static void Main(string[] args)  
    {  
        FileStream f = new FileStream("e:\\filestream-demo.txt", FileMode.OpenOrCreate);  
        int i = 0;  
        while ((i = f.ReadByte()) != -1)  
        {  
            Console.Write((char)i);  
        }  
        f.Close();  
    }  
}

MemoryStream:

MemoryStream是内存流,为系统内存提供读写操作,由于MemoryStream是通过无符号字节数组组成的

写入数据:

namespace MemoryStreamApp  
{  
    class Program  
    {  
        static void Main(string[] args)  
        {  
            //构造MemoryStream实例,并输出初始分配容量及使用大小  
            MemoryStream mem = new MemoryStream();  
            Console.WriteLine("初始分配容量:{0}" , mem.Capacity);  
            Console.WriteLine("初始使用量:{0}" , mem.Length);  
            //将待写入的数据从字符串转换为字节数组  
            UnicodeEncoding encoder = new UnicodeEncoding();  
            byte[] bytes = encoder.GetBytes("新增数据");  
            //向内存流中写入数据  
            for (int i = 1; i < 4; i++)  
            {  
                Console.WriteLine("第{0}次写入新数据", i);  
                mem.Write(bytes, 0, bytes.Length);  
            }  
            //写入数据后 MemoryStream 实例的容量和使用大小  
            Console.WriteLine("当前分配容量:{0}",mem.Capacity);  
            Console.WriteLine("当前使用量:{0}",mem.Length);  
            Console.ReadLine();  
        }  
    }  
}  

读取数据:

private void OnTestMemory()
         {
             //创建测试数据
              CreateExampleData();
             
             //创建内存流对象,初始分配50字节的缓冲区
              MemoryStream mem = new MemoryStream(50);
 
             //向内存流中写入字节数组的所有数据
              mem.Write(buffer,0,buffer.GetLength(0));
 
              MessageBox.Show("写入数据后的内存流长度:" + mem.Length.ToString());
              MessageBox.Show("分配给内存流的缓冲区大小:" + mem.Capacity.ToString());
 
              mem.SetLength(550);
 
              MessageBox.Show("调用SetLength方法后的内存流长度:" + mem.Length.ToString());
 
              mem.Capacity = 620;//此值不能小于Length属性
              MessageBox.Show("调用Capacity方法后缓冲区大小:" + mem.Capacity.ToString());
 
             //将读写指针移到距流开头10个字节的位置
              mem.Seek(10,SeekOrigin.Begin);
              MessageBox.Show(mem.ReadByte().ToString());
              mem.Close();
          }
//内存流的Length属性代表了其中存放的数据的真实长度,
//而Capacity属性则代表了分配给内存流的内存空间大小。
//可以使用字节数组创建一个固定大小的MemoryStream,
// MemoryStream mem
= new MemoryStream(buffer);
// 这时,无法再设置Capacity属性的大小。 还可以创建只读的内存流对象。
//MemoryStream mem
= new MemoryStream(buffer,false);

BufferedStream:

BufferedStream能够实现流的缓存,对缓存中的数据进行写入或是读取

读写:

using System;
using System.IO;
using System.Text;
class BufferedStreamTest
{
private static void AddText(BufferedStream bs, string value)
{
   byte[]info = new UTF8Encoding().GetBytes(value);
   bs.Write(info, 0, info.Length);
}
public static void Main()
{
   string path = "F:\\bsfile.txt";
   if (File.Exists(path))
    File.Delete(path);
   BufferedStream bs = new BufferedStream(File.Create(path));
   AddText(bs, "The first line ");
   AddText(bs, "123456789\r\n");
   AddText(bs, "The second line\r\n");
   AddText(bs, "Another line");
   bs.Close();
   bs = new BufferedStream(File.OpenRead(path));
   byte[]b = new byte[bs.Length];
   UTF8Encoding utf8 = new UTF8Encoding();
   while (bs.Read(b, 0, b.Length) > 0)
    Console.WriteLine(utf8.GetString(b));
    Console.ReadKey();
}
}

NetworkStream:

/// <summary>
   /// 服务端监听客户端信息,一旦有发送过来的信息,便立即处理
   /// </summary>
    class Program
    {
        //全局TcpClient
       static TcpClient client;
        //文件流建立到磁盘上的读写流
       static FileStream fs = new FileStream("E:\\abc.jpg", FileMode.Create);
        //buffer
       static int bufferlength = 200;
       static byte[] buffer = new byte[bufferlength];
        //网络流
       static NetworkStream ns;

        static void Main(string[] args)
        {
            ConnectAndListen();
        }

       static void ConnectAndListen() 
        {
           //服务端监听任何IP 但是端口号是80的连接
            TcpListener listener = new TcpListener(IPAddress.Any,80);
           //监听对象开始监听
            listener.Start();
            while(true)
            {
                Console.WriteLine("等待连接");
                //线程会挂在这里,直到客户端发来连接请求
                client = listener.AcceptTcpClient();
                Console.WriteLine("已经连接");
                //得到从客户端传来的网络流
                ns = client.GetStream();
                //如果网络流中有数据
                    if (ns.DataAvailable)
                    {
                        //同步读取网络流中的byte信息
                       // do
                      //  {
                      //  ns.Read(buffer, 0, bufferlength);
                      //} while (readLength > 0);

                        //异步读取网络流中的byte信息
                        ns.BeginRead(buffer, 0, bufferlength, ReadAsyncCallBack, null);
                    }
            }
        }

       /// <summary>
       /// 异步读取
       /// </summary>
       /// <param name="result"></param>
       static void ReadAsyncCallBack(IAsyncResult result) 
       {
           int readCount;
           //获得每次异步读取数量
           readCount = client.GetStream().EndRead(result);
           //如果全部读完退出,垃圾回收
           if (readCount < 1) 
           {
               client.Close();
               ns.Dispose();
               fs.Dispose();
               return; 
           }
          //将网络流中的图片数据片段顺序写入本地
           fs.Write(buffer, 0, 200);
           //再次异步读取
           ns.BeginRead(buffer, 0, 200, ReadAsyncCallBack, null);
       }
    }
class Program
    {
       /// <summary>
       /// 客户端
       /// </summary>
       /// <param name="args"></param>
        static void Main(string[] args)
        {
            SendImageToServer("xxx.jpg");
        }   

        static void SendImageToServer(string imgURl)
        {
            if (!File.Exists(imgURl)) return;
             //创建一个文件流打开图片
            FileStream fs = File.Open(imgURl, FileMode.Open);
            //声明一个byte数组接受图片byte信息
            byte[] fileBytes = new byte[fs.Length];
            using (fs)
            {
                //将图片byte信息读入byte数组中
                fs.Read(fileBytes, 0, fileBytes.Length);
                fs.Close();
            }
            //找到服务器的IP地址
            IPAddress address = IPAddress.Parse("127.0.0.1");
            //创建TcpClient对象实现与服务器的连接
            TcpClient client = new TcpClient();
            //连接服务器
            client.Connect(address, 80);
            using (client)
            {
                //连接完服务器后便在客户端和服务端之间产生一个流的通道
                NetworkStream ns = client.GetStream();
                using (ns)
                {
                    //通过此通道将图片数据写入网络流,传向服务器端接收
                   ns.Write(fileBytes, 0, fileBytes.Length);
                }
            }
        }
    }

PipeStream:

 PipeStream读取或写入来自不同进程的字节。

    private static byte[] matchSign = {9, 0, 9, 0};

    public static void Main()
    {
        string[] args = Environment.GetCommandLineArgs();
        if (args.Length < 2)
        {
            Process clientProcess = new Process();

            clientProcess.StartInfo.FileName = Environment.CommandLine;

            using (AnonymousPipeServerStream pipeServer =
                new AnonymousPipeServerStream(PipeDirection.In,
                HandleInheritability.Inheritable))
            {
                // Pass the client process a handle to the server.
                clientProcess.StartInfo.Arguments = pipeServer.GetClientHandleAsString();
                clientProcess.StartInfo.UseShellExecute = false;
                Console.WriteLine("[SERVER] Starting client process...");
                clientProcess.Start();

                pipeServer.DisposeLocalCopyOfClientHandle();

                try
                {
                    if (WaitForClientSign(pipeServer))
                    {
                        Console.WriteLine("[SERVER] Valid sign code received!");
                    }
                    else
                    {
                        Console.WriteLine("[SERVER] Invalid sign code received!");
                    }
                }
                catch (IOException e)
                {
                    Console.WriteLine("[SERVER] Error: {0}", e.Message);
                }
            }
            clientProcess.WaitForExit();
            clientProcess.Close();
            Console.WriteLine("[SERVER] Client quit. Server terminating.");
        }
        else
        {
            using (PipeStream pipeClient = new AnonymousPipeClientStream(PipeDirection.Out, args[1]))
            {
                try
                {
                    Console.WriteLine("[CLIENT] Sending sign code...");
                    SendClientSign(pipeClient);
                }
                catch (IOException e)
                {
                     Console.WriteLine("[CLIENT] Error: {0}", e.Message);
                }
            }
            Console.WriteLine("[CLIENT] Terminating.");
        }
    }

    private static bool WaitForClientSign(PipeStream inStream)
    {
        byte[] inSign = new byte[matchSign.Length];
        int len = inStream.Read(inSign, 0, matchSign.Length);
        bool valid = len == matchSign.Length;

        while (valid && len-- > 0)
        {
            valid = valid && (matchSign[len] == inSign[len]);
        }
        return valid;
    }

    private static void SendClientSign(PipeStream outStream)
    {
        outStream.Write(matchSign, 0, matchSign.Length);
    }

2.File类和Directory类

FIle类:

文件创建相关方法

  1. File.Create(String):在指定路径中创建或覆盖文件。
  2. File.Create(String, Int32):在指定路径中创建或覆盖文件,指定缓冲区大小。
  3. File.Create(String, Int32, FileOptions):创建或覆盖指定路径中的文件,指定缓冲区大小和一个描述如何创建或覆盖该文件的选项。
  4. File.CreateText(String) :创建或打开用于写入 UTF-8 编码文本的文件。 如果该文件已存在,将覆盖其内容。
  5. FileStream(String, FileMode, FileAccess, FileShare) :使用指定的路径、创建模式、读/写权限和共享权限创建 FileStream 类的新实例。
  6. FileStream(String, FileMode, FileAccess, FileShare, Int32, FileOptions) :使用指定的路径、创建模式、读/写和共享权限、其他FileStreams 可以具有的对此文件的访问权限、缓冲区大小和附加文件选项初始化 FileStream 类的新实例。
  7. FileStream(SafeFileHandle, FileAccess, Int32, Boolean):使用指定的读/写权限、缓冲区大小和同步或异步状态为指定的文件句柄初始化 FileStream 类的新实例。
  8. FileStream(String, FileMode, FileAccess):使用指定的路径、创建模式和读/写权限初始化 FileStream 类的新实例。 
  9. Directory.CreateDirectory(String):在指定路径中创建所有目录和子目录,除非它们已经存在。

文件删除相关方法

  1. File.Delete(String):删除指定的文件。
  2. Directory.Delete(String):从指定路径删除空目录。
  3. Directory.Delete(String, Boolean):删除指定的目录,并删除该目录中的所有子目录和文件(如果表示)。

文件操作判断相关方法

  1. File.Exists(String):确定指定的文件是否存在。

  2. Directory.Exists(String) :确定给定路径是否引用磁盘上的现有目录。

示例代码:

using System;
using System.IO;
class Test
{
    public static void Main()
    {
        string path = @"c:\temp\MyTest.txt";
        if (!File.Exists(path))
        {
            // 创建一个要写入的文件。
            using (StreamWriter sw = File.CreateText(path))
            {
                sw.WriteLine("Hello");
                sw.WriteLine("And");
                sw.WriteLine("Welcome");
            }
        }
        // 打开要从中读取的文件。
        using (StreamReader sr = File.OpenText(path))
        {
            string s;
            while ((s = sr.ReadLine()) != null)
            {
                Console.WriteLine(s);
            }
        }
    }
}

 

Directory类

  • 1、CreateDirectory       根据指定路径创建目录。有重载,允许一次过创建多个目录。 
  • 2、Delete             删除指定的目录。     有重载,指示目录有子目录的情况下,是否删除子目录。 true则连同子目录一起删除。flase则不删除目录,并返回                 一个异常。
  • 3、Exists             确定给定路径是否引用磁盘上的现有目录。 
  • 4、GetAccessControl       已重载。 返回某个目录的 Windows 访问控制列表 (ACL)。 
  • 5、GetCreationTime         获取目录的创建日期和时间。 
  • 6、GetCreationTimeUtc    获取目录创建的日期和时间,其格式为协调通用时间 (UTC)。 
  • 7、GetCurrentDirectory     获取应用程序的当前工作目录。 
  • 8、GetDirectories        已重载。 获取指定目录中子目录的名称(字符串数组)。支持用正则表达式匹配符合名称的目录名。 注意,只返回目录名,不返回文件名
  • 9、GetDirectoryRoot       返回指定路径的卷信息、根信息或两者同时返回。 
  • 10、GetFiles            已重载。 返回指定目录中的文件的名称(字符串数组)。注意只返回文件名,不返回目录    
  • 11、GetFileSystemEntries    已重载。 返回指定目录中所有文件和子目录的名称(字符串数组)。目录名与文件名一起返回。 支持正则表达式检索。
  • 12、GetLastAccessTime      返回上次访问指定文件或目录的日期和时间。 
  • 13、GetLastAccessTimeUtc   返回上次访问指定文件或目录的日期和时间,其格式为协调通用时间 (UTC)。 
  • 14、GetLastWriteTime      返回上次写入指定文件或目录的日期和时间。 
  • 15、GetLastWriteTimeUtc     返回上次写入指定文件或目录的日期和时间,其格式为协调通用时间 (UTC)。 
  • 16、GetLogicalDrives      检索此计算机上格式为“<驱动器号>:\”的逻辑驱动器的名称。 
  • 17、GetParent          检索指定路径的父目录,包括绝对路径和相对路径。 
  • 18、Move            将文件或目录及其内容移到新位置。 
  • 19、SetAccessControl     将 DirectorySecurity 对象描述的访问控制列表 (ACL) 项应用于指定的目录。 
  • 20、SetCreationTime      为指定的文件或目录设置创建日期和时间。 
  • 21、SetCreationTimeUtc     设置指定文件或目录的创建日期和时间,其格式为协调通用时间 (UTC)。 
  • 22、SetCurrentDirectory    将应用程序的当前工作目录设置为指定的目录。 
  • 23、SetLastAccessTime     设置上次访问指定文件或目录的日期和时间。 
  • 24、SetLastAccessTimeUtc  设置上次访问指定文件或目录的日期和时间,其格式为协调通用时间 (UTC)。 
  • 25、SetLastWriteTime     设置上次写入目录的日期和时间。 
  • 26、SetLastWriteTimeUtc    设置上次写入某个目录的日期和时间,其格式为协调通用时间 (UTC)。

示例:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
namespace Windows
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }
        private void button1_Click(object sender, EventArgs e)
        {
            //1、是否存在文件夹
            string aaaa = "H:\\NCRE";    //路径的正确写法
            if (Directory.Exists(aaaa))   //如果不存在就创建file文件夹
            {
                MessageBox.Show("存在文件夹!");
            }
            else
            {
                MessageBox.Show("不存在文件夹!");               
            }
        }
        private void button2_Click(object sender, EventArgs e)
        {
            //5、文件夹是否已经删除
            string aaaa = "H:\\1";    //路径的正确写法
            if (Directory.Exists(aaaa))   //判断是否存在
            {
                MessageBox.Show("文件夹还在,没有删除!");
            }
            else
            {
                MessageBox.Show("文件夹已经删除!");
            }
        }
        private void button3_Click(object sender, EventArgs e)
        {//6、从a移动到b,判断a没有了,b有了
            string aaaa = "D:\\NCRE"; //D盘是之前的路径
            string bbbb = "H:\\NCRE";//H盘是移动之后的路径
            if (!Directory.Exists(aaaa) & Directory.Exists(bbbb))   //如果没有在D,在H证明成功
            {
                MessageBox.Show("D盘没有,H盘有!移动成功!");
            }
            else
            {
                MessageBox.Show("移动失败!");
            }
        }
    }
}

 

3.Path类

常用方法

  1. Path.GetFullPath(file) 取全路径
  2. Path.GetFileName(file) 取文件名,包含扩展名
  3. Path.GetFileNameWithoutExtension(file) 取文件名,不包含扩展名
  4. Path.GetExtension(file) 取扩展名
  5. Path.GetDirectoryName(file) 取路径名
  6. Path.GetPathRoot(file) 取盘符
  7. Path.Combine(file1,file2) 合并2个路径

示例:

string str = @"C:UsersAdministratorDesktopceshi.txt";
            //获得文件名
            Console.WriteLine(Path.GetFileName(str));
            //获得不包含扩展名的文件名
            Console.WriteLine(Path.GetFileNameWithoutExtension(str));
            //获得文件所在文件夹的名称
            Console.WriteLine(Path.GetDirectoryName(str));
            //获得文件所在的全路径
            Console.WriteLine(Path.GetFullPath(str));
            //拼接路径字符串
            Console.WriteLine(Path.Combine(@"D:ab","c.txt"));
            Console.ReadKey();

属性或方法

  1. string ChangeExtension(string path, string extension) 更改路径字符串的扩展名
  2. string Combine(params string[] paths) 将字符串数组组合成一个路径
  3. string Combine(string path1, string path2) 将两个字符串组合成一个路径
  4. string GetDirectoryName(string path) 返回指定路径字符串的目录信息
  5. string GetExtension(string path) 返回指定路径字符串的扩展名
  6. string GetFileName(string path) 返回指定路径字符串的文件名和扩展名
  7. string GetFileNameWithoutExtension(string path) 返回不具有扩展名的指定路径字符串的文件名
  8. string GetFullPath(string path) 返回指定路径字符串的绝对路径
  9. char[] GetInvalidFileNameChars() 获取包含不允许在文件名中使用的字符的数组
  10. char[] GetInvalidPathChars() 获取包含不允许在路径名中使用的字符的数组
  11. string GetPathRoot(string path) 获取指定路径的根目录信息
  12. string GetRandomFileName() 返回随机文件夹名或文件名
  13. string GetTempPath() 返回当前用户的临时文件夹的路径
  14. bool HasExtension(string path) 返回路径是否包含文件的扩展名
  15. bool IsPathRooted(string path) 返回路径字符串是否包含根

5.StreamReader(快速读取文本文件)

StreamReader对象的创建方式非常类似于StreamWriter对象。创建它的最常见方式是使用前面创建的FileStream对象:

FileStream fs = new FileStream("test.txt",FileMode.Open);
StreamReader sr = new StreamReader(fs);

同StreamWriter一样,StreamReader类可以直接在包含具体文件路径的字符串中创建:

StreamReader sr = new StreamReader("test.txt");

 

示例:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
class Program
{
static void Main(string[]args)
{
   try
   {
    FileStream aFile = new FileStream(@"c:\祝福.txt", FileMode.Open);
    StreamReader sr = new StreamReader(aFile);
    string strLine = sr.ReadLine();
    while(strLine != null)
    {
     Console.WriteLine(strLine);
     strLine = sr.ReadLine();
    }
    sr.Close();
   }
   catch (IOException ex)
   {
    Console.WriteLine(ex.Message);
    Console.ReadLine();
    return ;
   }
Console.ReadKey();

}
}

 

 

6.StreamWriter(快速写入文本文件)

除了使用FileStream类读写文本文件,.net还提供了StreamWriter类和StreamReader类专门处理文本文件。

这两个类从底层封装了文件流,读写时不用重新编码,提供了更文件的读写方式。

StreamWriter类允许将字符和字符串写入到文件中,不必转换为字节,它处理底层的转换,向FileStream对象写入数据。

如果已经有了FileStream对象,则可以使用此对象来创建StreamWriter对象:

FileStream fs = new FileStream("test.txt",FileMode.CreateNew);
StreamWriter sw = new StreamWriter(fs);

也可以直接从文件中创建StreamWriter对象:

StreamWriter sw = new StreamWriter("test.txt",true);

此构造函数中有两个参数,一个是文件名,另一个是布尔值,这个布尔值规定创建对象的方式如下:

如果此值为false,则创建一个新文件,如果存在原文件,则覆盖。
如果此值为true,则打开文件保留原来数据,如果找不到文件,则创建新文件。

与 创建FileStream对象不同,创建StreamWriter对象不会提供一组类似的选项:

除了使用Boolean值添加到文件的末尾或创建新文件之外,

根本没有像FileStream类那样指定FileMode属性的选项。

而且,没有设置FileAccess属性的选项,因此总是有对文件的读/写权限。

为了使用高级参数,必须先在FileStream构造函数中指定这些参数,然后在FileStream对象中创建StreamWriter。

示例:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
class Program
{
static void Main(string[]args)
{
   try
   {
    FileStream aFile = new FileStream(@"c:\123\欢迎.txt", FileMode.OpenOrCreate);
    StreamWriter sw = new StreamWriter(aFile);
    sw.WriteLine("为今后我们之间的进一步合作,");
    sw.WriteLine("为我们之间日益增进的友谊,");
    sw.Write("为朋友们的健康幸福,");
    sw.Write("干杯!朋友!");
    sw.Close();
   }
   catch (IOException ex)
   {
    Console.WriteLine(ex.Message);
    Console.ReadLine();
    return ;
   }
}
}