c#之流以及类型
流是字节序列的抽象概念
1.File和FileInfo
File类是抽象类,其所有的方法都是静态的,而FileInfo类的所有方法都是实例方法。
先了解一下常用的2种编码
·GBK通常指GB2312编码 只支持简体中文字
·utf通常指UTF-8(解决国际上字符的一种多字节编码),支持简体中文字、繁体中文字、英文、日文、韩文等语言(支持文字更广)
·通常国内使用utf-8和gb2312,看自己需求选择。
https://blog.csdn.net/Deft_MKJing/article/details/79460485 参考
(1)创建文件
//创建一个默认UTF-8编码的xml StreamWriter fileStream = File.CreateText(@"D:\\文件\\Forms\\test.xml"); // 创建test.txt FileStream file = File.Create(@"D:\\文件集\\Forms\\test.txt", 100,FileOptions.Asynchronous);
(2)打开文件
//打开一个UTF-8编码的文件 StreamReader streamReader = File.OpenText(@"D:\\文件\\Forms\\test.xml"); //打开一个二进制文件 FileStream fileStream = File.Open(@"D:\\文件\\Forms\\test.xml",FileMode.Create,FileAccess.Read);
(3)获取当前路径下的不含子目录的文件和子目录
DirectoryInfo directoryInfo = new DirectoryInfo(direc); //获取子目录 返回当前目录下的子目录,就是当前目录下还有子目录的那些文件信息 DirectoryInfo[] directoryArr = directoryInfo.GetDirectories(); //GetFiles()方法获取的是文件,而不是文件夹,比如txt,图片,word这些文件。 FileInfo[] fileInfos = directoryInfo.GetFiles();
//找后缀名为docx和png的文件 var files = Directory.GetFiles(direc, "*.*", SearchOption.TopDirectoryOnly) .Where(s => s.EndsWith(".docx") || s.EndsWith(".png"));
(4)复制文件
可以使用File类的Copy方法和FileInfo的CopyTo方法。
//将test文件复制出test2文件,并且如果已经有test2文件的话进行覆盖 File.Copy(@"D:\\文件集\\test.txt", @"D:\\文件集\\test2.txt",true); FileInfo fileInfo = new FileInfo(@"D:\\文件集\\test.xml"); fileInfo.CopyTo(@"D:\\文件集\\test2.xml",true);
(5)删除文件
#region 文件的移动和删除 //原文件会自动删除 File.Move(@"D:\\文件集\\test.txt", @"D:\\文件集\\test3.txt"); FileInfo fileInfo1 = new FileInfo(@"D:\\文件集\\test.xml"); fileInfo1.MoveTo(@"D:\\文件集\\test4.xml"); File.Delete(@"D:\\文件集\\test3.txt"); FileInfo file = new FileInfo(@"D:\\文件集\\test4.xml"); file.Delete(); #endregion
2.目录管理
(1)创建目录
#region 创建目录 //创建一个文件夹TEST,并且在这个文件下面再创建一个test文件夹 Directory.CreateDirectory(@"D:\\TEST").CreateSubdirectory("test"); DirectoryInfo directoryInfo = new DirectoryInfo(@"D:\\TEST1"); directoryInfo.Create(); //在文件TEST1下创建子文件ttt directoryInfo.CreateSubdirectory("ttt"); #endregion
(2)删除目录
//删除空目录,如果不是空目录则会报错。 Directory.Delete("D:\\TEST1\\ttt"); //下面2种都会将包含TEST文件及其目录下的所有文件删除 Directory.Delete("D:\\TEST\\",true); Directory.Delete("D:\\TEST", true);
3.FileStream文件流 (内部已存在缓冲)
(1)用于在程序和文件之间传送字节或数据。
以只读的方式打开一个现有文件,并且在关闭文件之前禁止任何形式的共享。
#region 打开现有文件 FileStream fileStream = File.Open("D:\\文件集\\test2.txt",FileMode.Open,FileAccess.Read,FileShare.None); fileStream.Close(); FileStream file = new FileStream("D:\\文件集\\test2.xml", FileMode.Open, FileAccess.Read, FileShare.None); file.Close(); #endregion
实例:
class Program { static void Main(string[] args) { #region 创建test.txt文件。如果存在,先删除再创建。不存在直接创建。创建之后写入数据并读出。 //@是取消转义功能 string path = @"D:\文件集\test.txt"; if (File.Exists(path)) { File.Delete(path); } //创建文件 using (FileStream fileStream = File.Create(path)) { AddText(fileStream, "This is some text"); AddText(fileStream, "This is some more text"); AddText(fileStream, "\r\nand this is on a new line"); AddText(fileStream, "\r\n\r\nThe following is a subject of numbers:\r\n"); for (int i = 1; i <= 100; i++) { AddText(fileStream, i + " "); if (i % 10 == 0)//10个一行 { AddText(fileStream, "\r\n"); } } } //读取文件 using (FileStream file = File.OpenRead(path)) { byte[] b = new byte[file.Length]; UTF8Encoding uTF8 = new UTF8Encoding(true); while (file.Read(b, 0, b.Length)>0) { Console.WriteLine(uTF8.GetString(b)); } } #endregion } public static void AddText(FileStream file, string text) { //utf8编码的字符流 byte[] info = new UTF8Encoding(true).GetBytes(text); file.Write(info, 0, info.Length); } }
注意:在创建文件那里如果不使用using自动释放资源的话,需要加上
fileStream.Flush();
fileStream.Close();
因为FileStream是有缓冲区的,必须将缓冲区中的数据写入基础流中才行。
结果:
This is some textThis is some more text and this is on a new line The following is a subject of numbers: 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
4.文本文件的读写
除了使用FileStream类实现文件读写之外,还可以使用两个专门负责文本文件读取和写入操作的类。
具体的字节字符的区别可以在这里看一下 https://blog.csdn.net/yanyujingzhe/article/details/80437078
FileStream ,以字节流的方式进行文件的读写,用于字节的输入和输出,通常用于大文件(二进制文件)的读写 。
StreamReader和StreamWriter,通常用于对文本文件的读写,使用这个的好处是不同的文本文件有不同的编码格式,SteamReader会帮助我们自动处理,StreamWriter也可以指定写入文本的编码方式。
TextReader分别是StreamReader,StringReader的抽象基类
TextWriter是StreamWriter,StringWriter的抽象基类。
抽象Stream类的实现用于Unicode字符输出。抽象类TextReader,TextWriter则是用于Unicode字符输出。
所以,StreamReader是以一种特定的编码从字节流中读取字符。默认编码是UTF-8.
public override.Peek():返回下一个可用的字符,但不增加流指针,不使用这个字符。如果已经没有更多的可用字符号或此流不支持查找,则返回值为-1.
例子:
StreamReader stream = new StreamReader("D:\\文件集\\test.txt", Encoding.Default); //设置当前流的位置 stream.BaseStream.Seek(0,SeekOrigin.Current); while(stream.Peek()>-1) { Console.WriteLine(stream.ReadLine()); } Console.ReadKey();
注意seek方法,是设置当前流的位置,代表从哪个地方开始读取。
区别:
1 peek/read都是读取下一个字符
2 peek/read都是返回ascii码
3 peek方法调用后指针还是指向原来的字符,但是read调用后指向下一个。
string text = "test"; TextReader tr = new StringReader(text); while (tr.Peek() != -1) // Console.WriteLine(tr.Read()); //正常工作 Console.WriteLine(tr.Peek()); //死循环 Console.ReadKey();
上面代码中使用peek会死循环,因为peek的指针还是当前的,while中还是用peek的话就会一直循环的找当前下的指针,一直循环下去:116,116,116,116,,,,,,。
下面是正常情况下输出的对应的ASCII码。
116 101 115 116
注意下面的代码,其中AutoFlush如果设置成false,就不会将数据写入到文本中。
AutoFlush:改值指示StreamWriter是否在每次调用Write之后将缓冲区的数据刷新的基础流中,如果不刷新就相当于没有写入数据。
BaseStream就是操作streamreader的,返回的结果就是streamreader
string path = @"D:\文件集\test.txt"; if (File.Exists(path)) { File.Delete(path); } //创建文件 using (FileStream fileStream = File.Create(path)) { //转成StreamWriter操作 StreamWriter streamWriter = new StreamWriter(fileStream); streamWriter.BaseStream.Seek(0,SeekOrigin.End); streamWriter.AutoFlush = true; streamWriter.Write("this is test"); }
示例:
public override void Flush(); // 清理当前写入器的所有缓冲区,并使所有缓冲数据写入基础流。
private void 打开ToolStripMenuItem_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "文本文件(*.txt)|*.txt"; if(ofd.ShowDialog()==DialogResult.OK) { StreamReader reader = new StreamReader(ofd.FileName,Encoding.UTF8); textBox1.Text = ""; while (reader.Peek()>-1) { textBox1.Text = textBox1.Text + reader.ReadLine()+"\r\n"; } reader.Close(); } } private void 保存ToolStripMenuItem_Click(object sender, EventArgs e) { SaveFileDialog save = new SaveFileDialog(); //获取或设置当前文件名筛选器字符串,该字符串决定对话框的“另存为文件类型”或“文件类型”框中出现的选择内 save.Filter = ""; if(save.ShowDialog()==DialogResult.OK) { FileStream fileStream = new FileStream(save.FileName,FileMode.OpenOrCreate,FileAccess.Write); StreamWriter writer = new StreamWriter(fileStream,Encoding.UTF8); writer.Write(textBox1.Text); //没有这步是不会将数据写入到文本中的。 writer.Flush(); writer.Close(); } }
5.二进制文件的读写
BinaryWriter 作用是以二进制形式将基本数据类型的数据写入到流中,并支持用特定的编码写入字符串。
BinaryReader作用是用特定的编码从流中读取二进制数据并存放到基本数据类型的变量或数组中。
示例:
//创建文件流和二进制读写器对象 FileStream stream = new FileStream("D:\\文件集\\test2.txt", FileMode.Open); BinaryReader reader = new BinaryReader(stream); BinaryWriter writer = new BinaryWriter(stream, Encoding.Default); //写入数据 byte[] b = Encoding.UTF8.GetBytes(""); writer.Write(25); writer.Write(0.5M); writer.Write('d'); writer.Write("测试:test"); writer.Write("asds"); writer.Write(DateTime.Now.ToString()); //定位到流的开始位置。因为之前写入流数据的时候位置已经不是指向开始位置了。 stream.Seek(0,SeekOrigin.Begin); //依次读取各类型的数据 Console.WriteLine($"int:{reader.ReadInt32()},decimal:{reader.ReadDecimal()},char:{reader.ReadChar()},string:{reader.ReadString()},string:{ reader.ReadString() },date:{ reader.ReadString() }"); writer.Flush(); reader.Close(); writer.Close(); stream.Close(); Console.ReadKey();
注意,用文本编辑器直接打开二进制文件会乱码的。
结果:
int:25,decimal:0.5,char:d,string:测试:test,string:asds,date:2020/7/6 17:47:49
示例:以二进制的方式复制文件
static void Main(string[] args) { //定义缓冲区大小 int bufferSize = 1024; int bytes; byte[] buffer = new byte[bufferSize]; //文件源 FileStream inputFile = new FileStream("D:\\文件集\\test2.txt", FileMode.Open,FileAccess.Read); //写入的目标文件 FileStream outFile = new FileStream("D:\\文件集\\test.txt", FileMode.Create, FileAccess.Write); BinaryReader reader = new BinaryReader(inputFile); BinaryWriter writer = new BinaryWriter(outFile); //读入缓冲区的字节数。 如果有许多字节不可用,则该数目可能小于所请求的字节数;如果到达流的末尾,则该数目可能为零 //缓冲区的本质也就是提前申请的一块内存,现在可以直接获取一定量的数据到buffer中,如果不是这样的话就只能一个字符一个字符的读取。 while ((bytes=reader.Read(buffer,0,bufferSize))>0) { writer.Write(buffer,0, bytes); } reader.Close(); writer.Close(); inputFile.Close(); outFile.Close(); Console.ReadKey(); }
这个地址写的流很好:https://www.cnblogs.com/crazytomato/p/8274838.html
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术