赞
踩
File是静态类(无法被new)2.Directory文件夹static类
*主要静态方法:
*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): 读取文件中所有字节 费内存
/* if(File.Exists(@"d:\temp\a.pnproj")) { File.Delete(@"d:\temp\a.pnproj"); }*/ /* string[] lines = File.ReadAllLines(@"d:\temp\my.txt", Encoding.Default); for (int i = 0; i < lines.Length; i++) { Console.WriteLine("第"+i+"行:"+lines[i]); }*/ /* String s = File.ReadAllText(@"d:\temp\my.txt"); Console.WriteLine(s); */ // File.WriteAllText(@"d:\temp\test.txt", "123"); //File.AppendAllText(@"d:\temp\test.txt", "afasfsafadsf");
*CreateDirectory(string path):创建文件夹全路径3.流(Stream)
*void Delete(string path, bool recursive):删除文件夹path, recursive表示是否也删除子文件及子文件夹。
如果文件夹不为空, recursive=false,则抛异常;
*bool Exists(string path):判断文件夹是否存在
*IEnumerable<T> 泛型
*EnumerateFiles、 EnumerateDirectories遍历文件和文件夹;
// Directory.CreateDirectory(@"d:\temp\a\b\c"); //Directory.Delete(@"d:\temp\");//目录=文件夹,只能删除空文件夹 //Directory.Delete(@"D:\temp\redissessiontest", true); //递归删除 IEnumerable<string> files1 = //Directory.EnumerateFiles(@"d:\temp"); //Directory.EnumerateFiles(@"d:\temp","*.*",SearchOption.AllDirectories); Directory.EnumerateFiles(@"d:\temp", "*.avi", SearchOption.AllDirectories); IEnumerator<string> filesEnum1 = files1.GetEnumerator(); while (filesEnum1.MoveNext())//在结果中向下移动一条 { Console.WriteLine(filesEnum1.Current);//获得当前结果 }
*读取写入文件可用File.OpenRead(),File.OpenWrite()4.封装Copy流的方法并开发下载器
*File类提供了读写文本文件文件的方法,但是对于大文件不能用它提供的方法,占内存,需要一种“流式处理”的方法。
*.Net将IO操作(文件、网络等)简化成流模型Stream,是抽象类,网络、文件、加密等都是不同的子类,最常用的子类是FileStream、 MemoryStream 等。
*使用Stream的时候虽然可以:FileStream fs = new FileStream(....)但是Stream fs = new FileStream(...)更好,
在使用的变量类型提供的操作能满足的前提下,尽可能用父类、接口声明变量。
*FileStream写入文件
FileStream fs = new FileStream(@"d:\temp\1.txt",FileMode.Create); //可写成 Stream fs = File.OpenWrite(@"d:\temp\1.txt"); byte[] bytes1 = Encoding.Default.GetBytes("我是123"); //任何数据都是以byte为单位写入的。GetBytes获得字符串的byte表示形式 fs.Write(bytes1,0,bytes1.Length); Console.WriteLine(bytes1.Length);//=7,编码为Default,一个中文转换为两个byte,一个英文char为一个byte byte[] bytes2 = Encoding.UTF8.GetBytes("我是345"); fs.Write(bytes2,0,bytes2.Length);//在上一个Write的结尾继续写入 Console.WriteLine(bytes2.Length); //=9 编码为UTF8,一个中文转换为三个byte,一个英文char为一个byte fs.Close();//一定要用完了关闭
*Stream写入的单位是byte(字节),char转换为byte时候,一个英文char转换为一个byte(对应的ASCII码),
一个中文char转换为两个byte(*采用GBK编码时),试着输出bytes的长度。
*采用Default、UTF8、UTF32得到的字符串的byte[]长度不一样,因此说明不同类型编码在计算机中存储的不一样。
用什么编码写入就用什么编码读取,否则会有乱码的问题。
*FileStream释放:Dispose
static void Main(string[] args) { FileStream fs = null;//避免来不及创建就已经出异常 try { //如果new FileStream出异常,fs就不会被赋值,fs就有可能成为未赋值的变量 fs = new FileStream(@"c:\users\hua\desktop\a.txt", FileMode.Create); byte[] bytes = Encoding.Default.GetBytes("我是123"); fs.Write(bytes, 0, bytes.Length); Console.WriteLine(bytes.Length); byte[] bytes1 = Encoding.UTF8.GetBytes("我是123"); fs.Write(bytes1, 0, bytes1.Length); Console.WriteLine(bytes1.Length); } finally { if (fs != null) { //fs.Close();//这只是关闭,还可以再打开 fs.Dispose();//直接销毁fs所指向的对象 //所有实现IDisposable接口的类都需要调用Dispose方法来释放 Console.WriteLine("Dispose"); } } Console.ReadKey(); }
*using简化dispose资源释放
只要实现了idisposeable接口的类,用using都能帮我们进行资源回收
使用using操作FileStream时自动调用dispose释放资源
定义一个实现了IDisposable接口的类:
使用:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace IOTest2 { class MyFileStream:IDisposable { public MyFileStream() { //throw new Exception("初始化出错"); } public void Write(int i) { throw new Exception("Write出错"); Console.WriteLine("写入了"+i); } public void Dispose() { Console.WriteLine("Dispose被调用了,开始释放资源,虽然没什么可释放的"); } } }
可以同时声明多个资源:
using (MyFileStream fs1 = new MyFileStream()) { using (MyFileStream fs2 = new MyFileStream()) { fs2.Write(333); fs1.Write(5); } }
using其实就是编译器简化的try……finally操作,通过ILSpy反编译成IL可以看出来。
using (MyFileStream f1 = new MyFileStream()) using (MyFileStream f2 = new MyFileStream()) { fs2.Write(333); fs1.Write(5); }
using只是try……finally,如果需要catch,则自己把using try起来,在catch就行了
*FileStream.Read()读取文件:
*解释代码:
using (FileStream fs = new FileStream(@"D:\temp\1.txt", FileMode.Open)) //可写成 Stream fs = File.OpenRead(@"d:\temp\1.txt"); { byte[] bytes = new byte[4]; //fs.Read(bytes, 0, bytes.Length);//每次读取4个字节,下次Read的时候再读取最多4个byte //返回值为真正读取的字节数 int len; /* len = fs.Read(bytes, 0, bytes.Length); Console.WriteLine(len); len = fs.Read(bytes, 0, bytes.Length); Console.WriteLine(len); len = fs.Read(bytes, 0, bytes.Length); Console.WriteLine(len); */ //判断是否已经读到了最后 while ((len = fs.Read(bytes, 0, bytes.Length)) > 0) { //Console.WriteLine(len); //把byte数组转化为字符串 //string s = Encoding.Default.GetString(bytes); //读取到最后不够四个字节时,填入字节后,后面未填入位置还保留上一次读取到的字节 string s = Encoding.Default.GetString(bytes, 0, len);//len表示需读取的字节数 Console.WriteLine(s); } }
*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 中。
代码:
解释代码:
//100:9940ms //1024*50:7105ms //1024*1024:5024ms //每次读取数据越大,复制速度越快,也取决于磁盘的读取速度 Stopwatch sw = new Stopwatch();//计算运行的时间 sw.Start(); using (Stream inStream = new FileStream(@"e:\mysql-5.7.20-winx64.zip", FileMode.Open)) using (Stream outStream = new FileStream(@"g:\mysql-winx64.zip", FileMode.Create)) //可写成 File的静态方法,自动帮我们创建new FileStream //using (Stream inStream = File.OpenRead(@"e:\mysql-5.7.20-winx64.zip")) //using (Stream outStream = File.OpenWrite(@"g:\mysql-winx64.zip")) { //byte[] bytes = new byte[100]; byte[] bytes = new byte[1024*1024]; //缓冲区(buffer)的大小:过小:降低速度;过大:占用内存 int len; while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0) { outStream.Write(bytes, 0, len); } } sw.Stop(); Console.WriteLine("文件复制完成,耗时:"+sw.ElapsedMilliseconds+"毫秒"); 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类进行代码计时。
//.Net 4.0 之后自带 CopyTo()方法拷贝文件5.Reader、Writer (文本内容)
封装Copy方法:
文件下载器:
class CopyClass { public void Copy(Stream inStream, Stream outStream) { this.Copy(inStream,outStream,1024*1024); } public void Copy(Stream inStream, Stream outStream, int bufferSize) { byte[] bytes = new byte[bufferSize]; int len; while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0) { outStream.Write(bytes, 0, len); } Console.WriteLine("拷贝完成."); } } using (Stream inStream = new FileStream(@"d:\1.zip",FileMode.Open)) using (Stream outStream = new FileStream(@"d:\2.zip", FileMode.Create)) { Copy(inStream, outStream); }
WebRequest req = WebRequest.Create("http://www.baidu.com/img/bd_logo1.png"); using (WebResponse res = req.GetResponse()) using (Stream inStream = res.GetResponseStream()) using(Stream outStream = new FileStream(@"d:\2.png",FileMode.Create)) { Console.WriteLine(inStream.GetType());//Object o = new Person(); //Copy(inStream, outStream); inStream.CopyTo(outStream);//内置CopyTo()方法拷贝文件 }
编写一个程序,将d:\code目录下的所有.jpg文件复制到d:\code2目录下,并将文件的扩展名从.jpg改为.bmp(不用进行文件格式转换)。
//方法一: IEnumerable<string> files = Directory.EnumerateFiles(@"d:\壁纸", "*.jpg", SearchOption.AllDirectories); IEnumerator<string> filesEnum = files.GetEnumerator(); Directory.CreateDirectory(@"d:\code2");//创建code2文件夹 while (filesEnum.MoveNext()) { string path = filesEnum.Current;//获取文件路径 int dian = path.LastIndexOf('.'); int xie = path.LastIndexOf(@"\"); string name = path.Substring(xie + 1, dian - xie - 1);//获取文件名 using (Stream inStream = new FileStream(path, FileMode.Open)) using (Stream outStream = new FileStream(@"d:\code2\" + name + ".bmp", FileMode.Create))//改了扩展名,未改文件名 { byte[] bytes = new byte[1024 * 1024]; int len; while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0) { outStream.Write(bytes, 0, len); } } }
//方法二: string[] files1 = Directory.GetFiles(@"d:\壁纸", "*.jpg"); Directory.CreateDirectory(@"d:\code3");//创建code2文件夹 for (int i = 0; i < files1.Length; i++) { string file = files1[i]; //string filePath = Path.GetDirectoryName(file);//获取文件所在的文件夹路径 string fileNameWhthoutExt = Path.GetFileNameWithoutExtension(file);//获取没有扩展名的文件名 //string bmpFile = @"d:\code3\" + fileNameWhthoutExt + ".bmp"; string bmpFile = Path.Combine(@"d:\code3", fileNameWhthoutExt + ".bmp");//路径拼接,自动生成'\' using (Stream inStream = new FileStream(file, FileMode.Open)) using (Stream outStream = new FileStream(bmpFile, FileMode.Create)) { byte[] bytes = new byte[1024 * 1024]; int len; while ((len = inStream.Read(bytes, 0, bytes.Length)) > 0) { outStream.Write(bytes, 0, len); } } }
*直接用Stream进行文本文件的读写会比较麻烦,因为要考虑文件的编码、中文字符等的问题。
*StreamReader、StreamWriter是用来读写字符流(character stream)的类,会帮着自动处理麻烦的问题。
*演示:使用StreamWriter进行写:
using (Stream outStream = new FileStream(@"c:\1.txt", FileMode.Create)) using (StreamWriter sw = new StreamWriter(outStream,Encoding.Default)) { sw.Write("我是111"); sw.WriteLine("我是222"); sw.Write("我是333"); }*StreamWriter的伪代码解释原理:
*演示:使用StreamReader进行读:
class StreamWriter:IDisposable { private Stream stream; private Encoding encoding; public StreamWriter(Stream stream, Encoding encoding) { this.stream = stream; this.encoding = encoding; } public void Write(string s) { byte[] bytes = encoding.GetBytes(s); stream.Write(bytes, 0, bytes.Length); } public void WriteLine(string s) { byte[] bytes = encoding.GetBytes(s+"\r\n"); stream.Write(bytes, 0, bytes.Length); } public void Dispose() { } }
*StreamReader的伪代码解释原理:
using (Stream inStream=new FileStream(@"c:\users\hua\desktop\1.txt",FileMode.Open)) using (StreamReader reader=new StreamReader(inStream,Encoding.Default)) { /* string s = reader.ReadToEnd(); Console.WriteLine(s); */ /* string s = reader.ReadLine(); Console.WriteLine(s); */ string len; while((len=reader.ReadLine())!=null) { Console.WriteLine(len); } }
- class StreamReader : IDisposable
- {
- private Stream stream;
- private Encoding encoding;
- public StreamReader(Stream stream, Encoding encoding)
- {
- this.stream = stream;
- this.encoding = encoding;
- }
- public string ReadLine()//逐行读取
- {
- byte[] bytes = new byte[stream.Length];
- int len;
- while ((len = stream.Read(bytes, 0, bytes.Length)) > 0)
- {
- string s = encoding.GetString(bytes);
- return s;
- }
- return null;
- }
- public string ReadToEnd()//一次性读取
- {
- byte[] bytes = new byte[stream.Length];
- int len;
- string s = "";
- while ((len = stream.Read(bytes, 0, bytes.Length)) > 0)
- {
- s = s+ encoding.GetString(bytes);
- }
- return s;
- }
- public void Dispose()
- {
- }
- }
*编码6.泛型 List<T> <T>类型可以是Object
由于历史原因,汉字等如何在计算机中以字节存储存在着多种标准,最常见的是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构造函数决定。
1、数组长度是固定的,List<T>可以动态增删内容。List<T>是泛型的,可以在声明的时候指定放什么类型的数据。7.泛型字典Dictionary<K,V>: k,v类型可以是Object
2、如何遍历?
List<int> list = new List<int>(); list.Add(1); list.Add(2); list.AddRange(new int[] { 1, 2, 3, 4, 5, 6 }); list.AddRange(list);//另一个list
3、int[] nums = list.ToArray(); //List泛型集合可以转换为数组
for (int i = 0; i < list1.Count; i++) { int num = list1[i]; Console.WriteLine(num); }
List<string> listStr = new List<string>();
string[] str = listStr.ToArray();//根据当前List中的元素生成一个数组,之后的List变化与之无关
*Dictionary查询速度非常快:哈希算法8.foreach
*<k,v>k是键的类型(根据谁查),v是值的类型(查什么)
/* Dictionary<string,int> dict= new Dictionary<string, int> { }; dict.Add("abc", 123); dict.Add("aaa", 111); int i=dict["abc"];//根据key从字典中查数据 Console.WriteLine(i); */ //Dictionary<k,v>:k是键的类型(根据谁查),v是值的类型(查什么) Dictionary<string, string> dict = new Dictionary<string, string> { }; dict.Add("zs","张三"); //往字典中放数据(k,v) dict.Add("ls", "李四"); //dict.Add("ls", "李思");//如果key已经存在,则抛异常 dict["ls"] = "李思";//如果不存在key,则添加;如果存在则替换 dict["ww"] = "王五"; string s = dict["ls"];//根据key从字典中查数据 Console.WriteLine(s); Console.ReadKey();
*所有实现的IEnumerable接口的对象都可以使用foreach遍历
IEnumerable接口,有一个GetEnumerator的方法,返回一个实现IEnumerator接口的对象。
IEnumerator接口,有Current只读属性,MoveNext方法,Reset方法。
*第一次 var a in GetList() 时 调用 GetEnumerator 返回第一个对象并赋给a,
以后每次再执行 var a in GetList() 的时候 调用 MoveNext.直到循环结束.
期间GetList()方法只执行一次.
*注意:使用MoveNext()的方法讲解IEumerable只是在讲原理,实际开发中还是使用foreach
*除了使用for遍历,实现了IEumerable接口的对象还可以使用foreach遍历:
IEnumerable<string> files1 = Directory.EnumerateFiles(@"d:\temp", "*.avi", SearchOption.AllDirectories); IEnumerator<string> filesEnum1 = files1.GetEnumerator(); while (filesEnum1.MoveNext())//在结果中向下移动一条 { Console.WriteLine(filesEnum1.Current);//获得当前结果 }
*List<T>、 Dictionary<K,V> 等一切实现了IEumerable接口的对象都可以:
string[] strs = { "fasdfa","fsadf","222"}; foreach (string s in strs) { Console.WriteLine(s); }
*//foreach遍历字典实现原理
Dictionary<string, object> dict = new Dictionary<string, object>(); dict["rupeng"] = 888; dict["sina"] = "hehe"; dict["baidu"] = true; foreach (KeyValuePair<string, object> kv in dict)//KeyValuePair<string, object>:遍历字典时的类型 { Console.Write(kv.Key+"="+kv.Value+" "); } Console.WriteLine();
*for与foreach的区别:
IEnumerator<KeyValuePair<string,object>> iedict = dict.GetEnumerator(); while (iedict.MoveNext()) { KeyValuePair<string,object> kv = iedict.Current; Console.Write(kv.Key + "=" + kv.Value + " "); }
首先,for遍历条件可以多样化(倒序等),而foreach遍历只能从第一个到最后一个
第二,foreach语句是for语句的特殊简化版本,但是foreach语句并不能完全取代for语句,
需使用foreach遍历必须实现IEnumerable接口。然而,任何的foreach语句都可以改写为for语句版本。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。