当前位置:   article > 正文

.Net学习笔记8-.Net基础2 IO操作_off:0 ,len:5

off:0 ,len:5
1.File文件static类
File是静态类(无法被new)
*主要静态方法:
*void Delete(string path):删除文件;
*bool Exists(string path):判断文件是否存在;
*string[] ReadAllLines(string path):将文本文件中的内容读取到string数组中;Encoding.转换文字编码格式
*string ReadAllText(string path):将文本文件读取为一个字符串
*void WriteAllLines(string path, string[] contents):将string[]写入到文件中;
*void WriteAllText(string path, string contents):将字符串contents写入到文件path中。
*AppendAllText:向文件中附加内容;Copy复制文件;Move移动文件
*Stream File.OpenRead(string path) 打开文件读取, //path:文件路径
*Stream File.OpenWrite(string path) 打开文件写入, 
*byte[] File.ReadAllBytes(string path): 读取文件中所有字节  费内存
  1. /*
  2. if(File.Exists(@"d:\temp\a.pnproj"))
  3. {
  4. File.Delete(@"d:\temp\a.pnproj");
  5. }*/
  6. /*
  7. string[] lines = File.ReadAllLines(@"d:\temp\my.txt",
  8. Encoding.Default);
  9. for (int i = 0; i < lines.Length; i++)
  10. {
  11. Console.WriteLine("第"+i+"行:"+lines[i]);
  12. }*/
  13. /*
  14. String s = File.ReadAllText(@"d:\temp\my.txt");
  15. Console.WriteLine(s);
  16. */
  17. // File.WriteAllText(@"d:\temp\test.txt", "123");
  18. //File.AppendAllText(@"d:\temp\test.txt", "afasfsafadsf");

2.Directory文件夹static类
*CreateDirectory(string path):创建文件夹全路径
*void Delete(string path, bool recursive):删除文件夹path, recursive表示是否也删除子文件及子文件夹。
如果文件夹不为空, recursive=false,则抛异常;
*bool Exists(string path):判断文件夹是否存在
*IEnumerable<T>  泛型
*EnumerateFiles、 EnumerateDirectories遍历文件和文件夹;

  1. // Directory.CreateDirectory(@"d:\temp\a\b\c");
  2. //Directory.Delete(@"d:\temp\");//目录=文件夹,只能删除空文件夹
  3. //Directory.Delete(@"D:\temp\redissessiontest", true); //递归删除
  4. IEnumerable<string> files1 =
  5. //Directory.EnumerateFiles(@"d:\temp");
  6. //Directory.EnumerateFiles(@"d:\temp","*.*",SearchOption.AllDirectories);
  7. Directory.EnumerateFiles(@"d:\temp", "*.avi", SearchOption.AllDirectories);
  8. IEnumerator<string> filesEnum1 = files1.GetEnumerator();
  9. while (filesEnum1.MoveNext())//在结果中向下移动一条
  10. {
  11. Console.WriteLine(filesEnum1.Current);//获得当前结果
  12. }


3.流(Stream)
*读取写入文件可用File.OpenRead(),File.OpenWrite()
*File类提供了读写文本文件文件的方法,但是对于大文件不能用它提供的方法,占内存,需要一种“流式处理”的方法。
*.Net将IO操作(文件、网络等)简化成流模型Stream,是抽象类,网络、文件、加密等都是不同的子类,最常用的子类是FileStream、 MemoryStream 等。
*使用Stream的时候虽然可以:FileStream fs = new FileStream(....)但是Stream fs = new FileStream(...)更好,
   在使用的变量类型提供的操作能满足的前提下,尽可能用父类、接口声明变量。
*FileStream写入文件
  1. FileStream fs = new FileStream(@"d:\temp\1.txt",FileMode.Create);
  2. //可写成 Stream fs = File.OpenWrite(@"d:\temp\1.txt");
  3. byte[] bytes1 = Encoding.Default.GetBytes("我是123");
  4. //任何数据都是以byte为单位写入的。GetBytes获得字符串的byte表示形式
  5. fs.Write(bytes1,0,bytes1.Length);
  6. Console.WriteLine(bytes1.Length);//=7,编码为Default,一个中文转换为两个byte,一个英文char为一个byte
  7. byte[] bytes2 = Encoding.UTF8.GetBytes("我是345");
  8. fs.Write(bytes2,0,bytes2.Length);//在上一个Write的结尾继续写入
  9. Console.WriteLine(bytes2.Length); //=9 编码为UTF8,一个中文转换为三个byte,一个英文char为一个byte
  10. fs.Close();//一定要用完了关闭


*Stream写入的单位是byte(字节),char转换为byte时候,一个英文char转换为一个byte(对应的ASCII码),
一个中文char转换为两个byte(*采用GBK编码时),试着输出bytes的长度。
*采用Default、UTF8、UTF32得到的字符串的byte[]长度不一样,因此说明不同类型编码在计算机中存储的不一样。
用什么编码写入就用什么编码读取,否则会有乱码的问题。
*FileStream释放:Dispose
  1. static void Main(string[] args)
  2. {
  3. FileStream fs = null;//避免来不及创建就已经出异常
  4. try
  5. {
  6. //如果new FileStream出异常,fs就不会被赋值,fs就有可能成为未赋值的变量
  7. fs = new FileStream(@"c:\users\hua\desktop\a.txt", FileMode.Create);
  8. byte[] bytes = Encoding.Default.GetBytes("我是123");
  9. fs.Write(bytes, 0, bytes.Length);
  10. Console.WriteLine(bytes.Length);
  11. byte[] bytes1 = Encoding.UTF8.GetBytes("我是123");
  12. fs.Write(bytes1, 0, bytes1.Length);
  13. Console.WriteLine(bytes1.Length);
  14. }
  15. finally
  16. {
  17. if (fs != null)
  18. {
  19. //fs.Close();//这只是关闭,还可以再打开
  20. fs.Dispose();//直接销毁fs所指向的对象
  21. //所有实现IDisposable接口的类都需要调用Dispose方法来释放
  22. Console.WriteLine("Dispose");
  23. }
  24. }
  25. Console.ReadKey();
  26. }


*using简化dispose资源释放
只要实现了idisposeable接口的类,用using都能帮我们进行资源回收
使用using操作FileStream时自动调用dispose释放资源
定义一个实现了IDisposable接口的类:
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading.Tasks;
  6. namespace IOTest2
  7. {
  8. class MyFileStream:IDisposable
  9. {
  10. public MyFileStream()
  11. {
  12. //throw new Exception("初始化出错");
  13. }
  14. public void Write(int i)
  15. {
  16. throw new Exception("Write出错");
  17. Console.WriteLine("写入了"+i);
  18. }
  19. public void Dispose()
  20. {
  21. Console.WriteLine("Dispose被调用了,开始释放资源,虽然没什么可释放的");
  22. }
  23. }
  24. }
使用:
  1. using (MyFileStream fs1 = new MyFileStream())
  2. {
  3. using (MyFileStream fs2 = new MyFileStream())
  4. {
  5. fs2.Write(333);
  6. fs1.Write(5);
  7. }
  8. }
可以同时声明多个资源:
  1. using (MyFileStream f1 = new MyFileStream())
  2. using (MyFileStream f2 = new MyFileStream())
  3. {
  4. fs2.Write(333);
  5. fs1.Write(5);
  6. }
using其实就是编译器简化的try……finally操作,通过ILSpy反编译成IL可以看出来。
using只是try……finally,如果需要catch,则自己把using try起来,在catch就行了
*FileStream.Read()读取文件:
  1. using (FileStream fs = new FileStream(@"D:\temp\1.txt", FileMode.Open))
  2. //可写成 Stream fs = File.OpenRead(@"d:\temp\1.txt");
  3. {
  4. byte[] bytes = new byte[4];
  5. //fs.Read(bytes, 0, bytes.Length);//每次读取4个字节,下次Read的时候再读取最多4个byte
  6. //返回值为真正读取的字节数
  7. int len;
  8. /*
  9. len = fs.Read(bytes, 0, bytes.Length);
  10. Console.WriteLine(len);
  11. len = fs.Read(bytes, 0, bytes.Length);
  12. Console.WriteLine(len);
  13. len = fs.Read(bytes, 0, bytes.Length);
  14. Console.WriteLine(len);
  15. */
  16. //判断是否已经读到了最后
  17. while ((len = fs.Read(bytes, 0, bytes.Length)) > 0)
  18. {
  19. //Console.WriteLine(len);
  20. //把byte数组转化为字符串
  21. //string s = Encoding.Default.GetString(bytes);
  22. //读取到最后不够四个字节时,填入字节后,后面未填入位置还保留上一次读取到的字节
  23. string s = Encoding.Default.GetString(bytes, 0, len);//len表示需读取的字节数
  24. Console.WriteLine(s);
  25. }
  26. }
*解释代码:
*byte[] bytes = new byte[4]:每次读取多少字节数据,不能一次把一个文件读取出来,否则太占用内存,因为数组是占用内存的。这个就叫“缓冲区”, 设的太小CPU/硬盘会很忙、设的太大内存会很“撑”。
*while((len = fs.Read(bytes, 0, bytes.Length))>0)继续从流中读取最多bytes长度那么多字节的数据拷贝到bytes数组中。下次read是从上次最后一个   read最后的位置之后开始读,一次次的读取read方法返回这次一共读了多少字节(比如遇到最后一次读取,可能读不满),一旦返回的=0了就说明  读完了。对于基本数值类型数组,没有被赋值初始值就是0,因为没被填满的数组位置是0。
*String s = Encoding.Default.GetString(bytes,0,len); 把byte[]转换为对应String,考虑bytes没有充分利用的情况
*字节流不适合读取内容中包含文本文档,容易造成数据错乱(byte[]中含有一半的汉字)。要用后面讲的StreamReader。
*复制文件
使用两个FileStream 配合完成文件拷贝:从源文件的FileStream 中读取一部分内容,再写入到目标文件的FileStream 中。
代码:
  1. //100:9940ms
  2. //1024*50:7105ms
  3. //1024*1024:5024ms
  4. //每次读取数据越大,复制速度越快,也取决于磁盘的读取速度
  5. Stopwatch sw = new Stopwatch();//计算运行的时间
  6. sw.Start();
  7. using (Stream inStream = new FileStream(@"e:\mysql-5.7.20-winx64.zip", FileMode.Open))
  8. using (Stream outStream = new FileStream(@"g:\mysql-winx64.zip", FileMode.Create))
  9. //可写成 File的静态方法,自动帮我们创建new FileStream
  10. //using (Stream inStream = File.OpenRead(@"e:\mysql-5.7.20-winx64.zip"))
  11. //using (Stream outStream = File.OpenWrite(@"g:\mysql-winx64.zip"))
  12. {
  13. //byte[] bytes = new byte[100];
  14. byte[] bytes = new byte[1024*1024];
  15. //缓冲区(buffer)的大小:过小:降低速度;过大:占用内存
  16. int len;
  17. while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0)
  18. {
  19. outStream.Write(bytes, 0, len);
  20. }
  21. }
  22. sw.Stop();
  23. Console.WriteLine("文件复制完成,耗时:"+sw.ElapsedMilliseconds+"毫秒");
  24. Console.ReadKey();
解释代码:
1、 while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0)
把读取并且返回读取的长度给len,然后判断len的值综合为了一句话。复习:赋值表达式的值就是赋值后的变量的值。
2、 outStream.Write(bytes, 0, len);:
每次write都会从上次write的最后位置接着写入。将byte[]数组b写入outStream中,off代码距离当前写入位置的偏移量,一般写0,len代表写入多长。缓冲 区先设置为50,再修改为1M,体会速度的变化。使用Stopwatch类进行代码计时。
4.封装Copy流的方法并开发下载器
//.Net 4.0 之后自带  CopyTo()方法拷贝文件
封装Copy方法:
  1. class CopyClass
  2. {
  3. public void Copy(Stream inStream, Stream outStream)
  4. {
  5. this.Copy(inStream,outStream,1024*1024);
  6. }
  7. public void Copy(Stream inStream, Stream outStream, int bufferSize)
  8. {
  9. byte[] bytes = new byte[bufferSize];
  10. int len;
  11. while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0)
  12. {
  13. outStream.Write(bytes, 0, len);
  14. }
  15. Console.WriteLine("拷贝完成.");
  16. }
  17. }
  18. using (Stream inStream = new FileStream(@"d:\1.zip",FileMode.Open))
  19. using (Stream outStream = new FileStream(@"d:\2.zip", FileMode.Create))
  20. {
  21. Copy(inStream, outStream);
  22. }
文件下载器:
  1. WebRequest req = WebRequest.Create("http://www.baidu.com/img/bd_logo1.png");
  2. using (WebResponse res = req.GetResponse())
  3. using (Stream inStream = res.GetResponseStream())
  4. using(Stream outStream = new FileStream(@"d:\2.png",FileMode.Create))
  5. {
  6. Console.WriteLine(inStream.GetType());//Object o = new Person();
  7. //Copy(inStream, outStream);
  8. inStream.CopyTo(outStream);//内置CopyTo()方法拷贝文件
  9. }


编写一个程序,将d:\code目录下的所有.jpg文件复制到d:\code2目录下,并将文件的扩展名从.jpg改为.bmp(不用进行文件格式转换)。
  1. //方法一:
  2. IEnumerable<string> files = Directory.EnumerateFiles(@"d:\壁纸", "*.jpg", SearchOption.AllDirectories);
  3. IEnumerator<string> filesEnum = files.GetEnumerator();
  4. Directory.CreateDirectory(@"d:\code2");//创建code2文件夹
  5. while (filesEnum.MoveNext())
  6. {
  7. string path = filesEnum.Current;//获取文件路径
  8. int dian = path.LastIndexOf('.');
  9. int xie = path.LastIndexOf(@"\");
  10. string name = path.Substring(xie + 1, dian - xie - 1);//获取文件名
  11. using (Stream inStream = new FileStream(path, FileMode.Open))
  12. using (Stream outStream = new FileStream(@"d:\code2\" + name + ".bmp", FileMode.Create))//改了扩展名,未改文件名
  13. {
  14. byte[] bytes = new byte[1024 * 1024];
  15. int len;
  16. while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0)
  17. {
  18. outStream.Write(bytes, 0, len);
  19. }
  20. }
  21. }
  1. //方法二:
  2. string[] files1 = Directory.GetFiles(@"d:\壁纸", "*.jpg");
  3. Directory.CreateDirectory(@"d:\code3");//创建code2文件夹
  4. for (int i = 0; i < files1.Length; i++)
  5. {
  6. string file = files1[i];
  7. //string filePath = Path.GetDirectoryName(file);//获取文件所在的文件夹路径
  8. string fileNameWhthoutExt = Path.GetFileNameWithoutExtension(file);//获取没有扩展名的文件名
  9. //string bmpFile = @"d:\code3\" + fileNameWhthoutExt + ".bmp";
  10. string bmpFile = Path.Combine(@"d:\code3", fileNameWhthoutExt + ".bmp");//路径拼接,自动生成'\'
  11. using (Stream inStream = new FileStream(file, FileMode.Open))
  12. using (Stream outStream = new FileStream(bmpFile, FileMode.Create))
  13. {
  14. byte[] bytes = new byte[1024 * 1024];
  15. int len;
  16. while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0)
  17. {
  18. outStream.Write(bytes, 0, len);
  19. }
  20. }
  21. }



5.Reader、Writer (文本内容)
*直接用Stream进行文本文件的读写会比较麻烦,因为要考虑文件的编码、中文字符等的问题。
*StreamReader、StreamWriter是用来读写字符流(character stream)的类,会帮着自动处理麻烦的问题。
*演示:使用StreamWriter进行写:
  1. using (Stream outStream = new FileStream(@"c:\1.txt", FileMode.Create))
  2. using (StreamWriter sw = new StreamWriter(outStream,Encoding.Default))
  3. {
  4. sw.Write("我是111");
  5. sw.WriteLine("我是222");
  6. sw.Write("我是333");
  7. }
*StreamWriter的伪代码解释原理:
  1. class StreamWriter:IDisposable
  2. {
  3. private Stream stream;
  4. private Encoding encoding;
  5. public StreamWriter(Stream stream, Encoding encoding)
  6. {
  7. this.stream = stream;
  8. this.encoding = encoding;
  9. }
  10. public void Write(string s)
  11. {
  12. byte[] bytes = encoding.GetBytes(s);
  13. stream.Write(bytes, 0, bytes.Length);
  14. }
  15. public void WriteLine(string s)
  16. {
  17. byte[] bytes = encoding.GetBytes(s+"\r\n");
  18. stream.Write(bytes, 0, bytes.Length);
  19. }
  20. public void Dispose()
  21. {
  22. }
  23. }
*演示:使用StreamReader进行读:
  1. using (Stream inStream=new FileStream(@"c:\users\hua\desktop\1.txt",FileMode.Open))
  2. using (StreamReader reader=new StreamReader(inStream,Encoding.Default))
  3. {
  4. /*
  5. string s = reader.ReadToEnd();
  6. Console.WriteLine(s);
  7. */
  8. /*
  9. string s = reader.ReadLine();
  10. Console.WriteLine(s);
  11. */
  12. string len;
  13. while((len=reader.ReadLine())!=null)
  14. {
  15. Console.WriteLine(len);
  16. }
  17. }
*StreamReader的伪代码解释原理:
  1. class StreamReader : IDisposable
  2. {
  3. private Stream stream;
  4. private Encoding encoding;
  5. public StreamReader(Stream stream, Encoding encoding)
  6. {
  7. this.stream = stream;
  8. this.encoding = encoding;
  9. }
  10. public string ReadLine()//逐行读取
  11. {
  12. byte[] bytes = new byte[stream.Length];
  13. int len;
  14. while ((len = stream.Read(bytes, 0, bytes.Length)) > 0)
  15. {
  16. string s = encoding.GetString(bytes);
  17. return s;
  18. }
  19. return null;
  20. }
  21. public string ReadToEnd()//一次性读取
  22. {
  23. byte[] bytes = new byte[stream.Length];
  24. int len;
  25. string s = "";
  26. while ((len = stream.Read(bytes, 0, bytes.Length)) > 0)
  27. {
  28. s = s+ encoding.GetString(bytes);
  29. }
  30. return s;
  31. }
  32. public void Dispose()
  33. {
  34. }
  35. }
*编码
由于历史原因,汉字等如何在计算机中以字节存储存在着多种标准,最常见的是GB2312(国家标准,表示汉字)、
GBK(GB2312的扩展,还能表示繁体字等)、UTF-8(国际标准)、UTF-16等。
举例:百度首页是采用UTF-8存储,点右键修改为GBK编码就会乱码;qq.com首页是采用GBK存储,修改为UTF-8也会乱码。
再比如记事本保存文件的时候也可以选择编码:ANSI表示采用当前操作系统的默认编码,如果是中文Windows,默认就是GBK。
用什么编码保存就用什么编码读取,就不会乱码!由StreamWriter 构造函数决定(为什么?)new StreamWriter(outStream, Encoding.UTF8)。
怎么初步判断用什么编码,记事本“另存为”。
读取的编码由StreamReader构造函数决定。
6.泛型 List<T> <T>类型可以是Object
1、数组长度是固定的,List<T>可以动态增删内容。List<T>是泛型的,可以在声明的时候指定放什么类型的数据。
  1. List<int> list = new List<int>();
  2. list.Add(1);
  3. list.Add(2);
  4. list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 });
  5. list.AddRange(list);//另一个list
2、如何遍历?
  1. for (int i = 0; i < list1.Count; i++)
  2. {
  3. int num = list1[i];
  4. Console.WriteLine(num);
  5. }
3、int[] nums = list.ToArray(); //List泛型集合可以转换为数组
List<string> listStr = new List<string>();
string[] str = listStr.ToArray();//根据当前List中的元素生成一个数组,之后的List变化与之无关
7.泛型字典Dictionary<K,V>: k,v类型可以是Object
*Dictionary查询速度非常快:哈希算法
*<k,v>k是键的类型(根据谁查),v是值的类型(查什么)
  1. /*
  2. Dictionary<string,int> dict= new Dictionary<string, int> { };
  3. dict.Add("abc", 123);
  4. dict.Add("aaa", 111);
  5. int i=dict["abc"];//根据key从字典中查数据
  6. Console.WriteLine(i);
  7. */
  8. //Dictionary<k,v>:k是键的类型(根据谁查),v是值的类型(查什么)
  9. Dictionary<string, string> dict = new Dictionary<string, string> { };
  10. dict.Add("zs","张三"); //往字典中放数据(k,v)
  11. dict.Add("ls", "李四");
  12. //dict.Add("ls", "李思");//如果key已经存在,则抛异常
  13. dict["ls"] = "李思";//如果不存在key,则添加;如果存在则替换
  14. dict["ww"] = "王五";
  15. string s = dict["ls"];//根据key从字典中查数据
  16. Console.WriteLine(s);
  17. Console.ReadKey();
8.foreach
*所有实现的IEnumerable接口的对象都可以使用foreach遍历
IEnumerable接口,有一个GetEnumerator的方法,返回一个实现IEnumerator接口的对象。
IEnumerator接口,有Current只读属性,MoveNext方法,Reset方法。
*第一次 var a in GetList() 时 调用 GetEnumerator 返回第一个对象并赋给a,
以后每次再执行 var a in GetList() 的时候 调用 MoveNext.直到循环结束.
期间GetList()方法只执行一次.
*注意:使用MoveNext()的方法讲解IEumerable只是在讲原理,实际开发中还是使用foreach
  1. IEnumerable<string> files1 = Directory.EnumerateFiles(@"d:\temp", "*.avi", SearchOption.AllDirectories);
  2. IEnumerator<string> filesEnum1 = files1.GetEnumerator();
  3. while (filesEnum1.MoveNext())//在结果中向下移动一条
  4. {
  5. Console.WriteLine(filesEnum1.Current);//获得当前结果
  6. }
*除了使用for遍历,实现了IEumerable接口的对象还可以使用foreach遍历:
  1. string[] strs = { "fasdfa","fsadf","222"};
  2. foreach (string s in strs)
  3. {
  4. Console.WriteLine(s);
  5. }
*List<T>、 Dictionary<K,V> 等一切实现了IEumerable接口的对象都可以:
  1. Dictionary<string, object> dict = new Dictionary<string, object>();
  2. dict["rupeng"] = 888;
  3. dict["sina"] = "hehe";
  4. dict["baidu"] = true;
  5. foreach (KeyValuePair<string, object> kv in dict)//KeyValuePair<string, object>:遍历字典时的类型
  6. {
  7. Console.Write(kv.Key+"="+kv.Value+" ");
  8. }
  9. Console.WriteLine();
*//foreach遍历字典实现原理
  1. IEnumerator<KeyValuePair<string,object>> iedict = dict.GetEnumerator();
  2. while (iedict.MoveNext())
  3. {
  4. KeyValuePair<string,object> kv = iedict.Current;
  5. Console.Write(kv.Key + "=" + kv.Value + " ");
  6. }
*for与foreach的区别:
首先,for遍历条件可以多样化(倒序等),而foreach遍历只能从第一个到最后一个
第二,foreach语句是for语句的特殊简化版本,但是foreach语句并不能完全取代for语句,
需使用foreach遍历必须实现IEnumerable接口。然而,任何的foreach语句都可以改写为for语句版本。
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/367283
推荐阅读
相关标签
  

闽ICP备14008679号