首页 > 代码库 > IOSerialize(序列化)
IOSerialize(序列化)
在讲序列化和反序列化之前,先来阐述文件夹/文件 检查、新增、复制、移动、删除,
Directory和DirectotyInfo这两个特性主要是对文件夹进行操作
首先检测文件夹是否存在
if (!Directory.Exists(LogPath))//检测文件夹是否存在 { } DirectoryInfo directory = new DirectoryInfo(LogPath);//不存在不报错 注意exists属性 Console.WriteLine(string.Format("{0} {1} {2}", directory.FullName, directory.CreationTime, directory.LastWriteTime));
但是需要注意的是,通过new形式的判断,假如文件不存在也是不会报错的,不仅不报错,而且输出文件信息也是照样可以输出的。
这里的LogPath是文件夹的地址,通过配置文件配置
<appSettings> <add key="LogPath" value="http://www.mamicode.com/D:/C#learning/Log/"/> <add key="LogMovePath" value="http://www.mamicode.com/D:/C#learning/LogMove/"/> </appSettings>
private static string LogPath = ConfigurationManager.AppSettings["LogPath"];
private static string LogMovePath = ConfigurationManager.AppSettings["LogMovePath"];
对文件夹进行创建,移动,删除。
if (!Directory.Exists(LogPath)) { DirectoryInfo directoryInfo = Directory.CreateDirectory(LogPath);//一次性创建全部的子路径 Directory.Move(LogPath, LogMovePath);//移动 原文件夹就不在了 Directory.Delete(LogMovePath);//删除 }
注意这里对文件夹删除时,是不会提示你是否删除的,所以如果文件夹地址写错,把重要文件给删除了,就后悔莫及了,不过这里的删除还是删除到回收站
下面讲讲对文件的一些操作,和几种输入流
string fileName = Path.Combine(LogPath, "log.txt");//拼装文件路径 string fileNameCopy = Path.Combine(LogPath, "logCopy.txt"); string fileNameMove = Path.Combine(LogPath, "logMove.txt"); bool isExists = File.Exists(fileName); if (!isExists) { Directory.CreateDirectory(LogPath);//在新建文件之前,一定要创建对应的文件夹 using (FileStream fileStream = File.Create(fileName))//打开文件流 (创建文件并写入) { string name = "12345564423432490"; byte[] bytes = Encoding.Default.GetBytes(name); fileStream.Write(bytes, 0, bytes.Length); fileStream.Flush(); } }
这种是以字节的形式写入。
using (FileStream fileStream = File.Create(fileName))//打开文件流 (创建文件并写入) { StreamWriter sw = new StreamWriter(fileStream); sw.WriteLine("1234567890"); sw.Flush(); }
注意:使用这两种方式写入时,新的写入会覆盖以前文件的内容
只有用AppendText方式才会在原来的基础上追加信息。
using (StreamWriter sw = File.AppendText(fileName))//流写入器(创建/打开文件并写入) { string msg = "今天是晴天"; sw.WriteLine(msg); sw.Flush(); }
对文件已经可以写入了,那么如何读取出来呢。
1)ReadAllLines,把文件中的所有行一次读取出来,然后通过遍历的方式,遍历所有行
foreach (string result in File.ReadAllLines(fileName)) { Console.WriteLine(result); }
2)ReadAllText,读取所有文本信息
string sResult = File.ReadAllText(fileName);
3)ReadAllBytes。通过字节的形式读取,然后编码转译
Byte[] byteContent = File.ReadAllBytes(fileName); string sResultByte = System.Text.Encoding.UTF8.GetString(byteContent);
还有一种分批读取,因为当文件非常大时,你直接打开文件,打开的软件是直接会奔溃的。这个时候就需要分批读取
using (FileStream stream = File.OpenRead(fileName))//分批读取 { int length = 5; int result = 0; do { byte[] bytes = new byte[length]; result = stream.Read(bytes, 0, 5); for (int i = 0; i < result; i++) { Console.WriteLine(bytes[i].ToString()); } } while (length == result); }
还有一些简单的对文件进行操作,
File.Copy(fileName, fileNameCopy); File.Move(fileName, fileNameMove); File.Delete(fileNameCopy); File.Delete(fileNameMove);//尽量不要delete
在这里简单介绍一下DriveInfo,对本地盘的信息操作,作为一个小知识点,有需要的时候再来看。
{//DriveInfo DriveInfo[] drives = DriveInfo.GetDrives(); foreach (DriveInfo drive in drives) { if (drive.IsReady) Console.WriteLine("类型:{0} 卷标:{1} 名称:{2} 总空间:{3} 剩余空间:{4}", drive.DriveType, drive.VolumeLabel, drive.Name, drive.TotalSize, drive.TotalFreeSpace); else Console.WriteLine("类型:{0} is not ready", drive.DriveType); } } { Console.WriteLine(Path.GetDirectoryName(LogPath)); //返回目录名,需要注意路径末尾是否有反斜杠对结果是有影响的 Console.WriteLine(Path.GetDirectoryName(@"d:\\abc")); //将返回 d: Console.WriteLine(Path.GetDirectoryName(@"d:\\abc\"));// 将返回 d:\abc Console.WriteLine(Path.GetRandomFileName());//将返回随机的文件名 Console.WriteLine(Path.GetFileNameWithoutExtension("d:\\abc.txt"));// 将返回abc Console.WriteLine(Path.GetInvalidPathChars());// 将返回禁止在路径中使用的字符 Console.WriteLine(Path.GetInvalidFileNameChars());//将返回禁止在文件名中使用的字符 Console.WriteLine(Path.Combine(LogPath, "log.txt"));//合并两个路径 }
最后附加一个简单写日志功能的代码,作为了解,不过现在一般都是通过log4net插件来写。
public static void Log(string msg) { StreamWriter sw = null; try { string fileName = "log.txt"; string totalPath = Path.Combine(LogPath, fileName); if (!Directory.Exists(LogPath)) { Directory.CreateDirectory(LogPath); } sw = File.AppendText(totalPath); sw.WriteLine(string.Format("{0}:{1}", DateTime.Now, msg)); sw.WriteLine("***************************************************"); } catch (Exception ex) { Console.WriteLine(ex.Message);//log //throw ex; //throw new exception("这里异常"); } finally { if (sw != null) { sw.Flush(); sw.Close(); sw.Dispose(); } } }
递归读取文件夹信息。
有一个需求,给你一个文件夹的根目录,怎么得出这个文件夹一共有多少个子文件夹。
public class Recursion { /// <summary> /// 找出全部的子文件夹 /// </summary> /// <param name="rootPath">根目录</param> /// <returns></returns> public static List<DirectoryInfo> GetAllDirectory(string rootPath) { List<DirectoryInfo> directoryList = new List<DirectoryInfo>(); DirectoryInfo directory = new DirectoryInfo(rootPath); //directoryList.Add(directory); //directoryList.AddRange(directory.GetDirectories()); //foreach (var child in directory.GetDirectories()) //{ // directoryList.AddRange(child.GetDirectories()); // foreach (var grand in child.GetDirectories()) // { // } //} GetChildDirectory(directoryList, directory); return directoryList; } /// <summary> /// 找出某个文件夹的子文件夹,,放入集合 /// /// 递归:方法自身调用自身 /// </summary> /// <param name="directoryList"></param> /// <param name="parentDirectory"></param> private static void GetChildDirectory(List<DirectoryInfo> directoryList, DirectoryInfo parentDirectory) { directoryList.AddRange(parentDirectory.GetDirectories()); if (parentDirectory.GetDirectories() != null && parentDirectory.GetDirectories().Length > 0) { foreach (var directory in parentDirectory.GetDirectories()) { GetChildDirectory(directoryList, directory); } } } }
这里需要注意的是,使用递归,一定要给个跳出循环的条件,要不然会一直死循环。
******************************************************************************************************
前面阐述了这么多,终于到了序列化和反序列化了。
那么什么是序列化和反序列化呢,简单来讲就是实体和字符串的转化
(1).NET支持对象序列化的几种方式
二进制序列化:对象序列化之后是二进制形式的,通过BinaryFormatter类来实现的,这个类位于System.Runtime.Serialization.Formatters.Binary命名空间下。
SOAP序列化:对象序列化之后的结果符合SOAP协议,也就是可以通过SOAP 协议传输,通过System.Runtime.Serialization.Formatters.Soap命名空间下的SoapFormatter类来实现的。
XML序列化:对象序列化之后的结果是XML形式的,通过XmlSerializer 类来实现的,这个类位于System.Xml.Serialization命名空间下。XML序列化不能序列化私有数据。
在序列化之前先写一个实体类,作为例子
[Serializable] //必须添加序列化特性 public class Person { [NonSerialized] public int Id = 1; public string Name { get; set; } public string Sex { get; set; } } [Serializable] //必须添加序列化特性 public class Programmer : Person { private string Language { get; set; } }
public static List<Programmer> BuildProgrammerList() { #region data prepare List<Programmer> list = new List<Programmer>(); list.Add(new Programmer() { Id = 1, Name = "mi", Sex = "男" }); list.Add(new Programmer() { Id = 1, Name = "xi", Sex = "男" }); list.Add(new Programmer() { Id = 1, Name = "ma", Sex = "男" }); #endregion return list; }
1)二进制序列化
关键字:Serialize
string fileName = Path.Combine(CurrentDataPath, @"BinarySerialize.txt");//文件名称与路径 Directory.CreateDirectory(CurrentDataPath); using (Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite)) {//需要一个stream,这里是直接写入文件了 List<Programmer> pList = DataFactory.BuildProgrammerList(); BinaryFormatter binFormat = new BinaryFormatter();//创建二进制序列化器 binFormat.Serialize(fStream, pList); }
这个时候就会创建一个二进制的文件
反序列化:通过关键字Deserialize
using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite)) {//需要一个stream,这里是来源于文件 BinaryFormatter binFormat = new BinaryFormatter();//创建二进制序列化器 //使用二进制反序列化对象 fStream.Position = 0;//重置流位置 List<Programmer> pList = (List<Programmer>)binFormat.Deserialize(fStream);//反序列化对象 }
SOAP序列化:
public static void SoapSerialize() { //使用Soap序列化对象 string fileName = Path.Combine(CurrentDataPath, @"SoapSerialize.txt");//文件名称与路径 using (Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite)) { List<Programmer> pList = DataFactory.BuildProgrammerList(); SoapFormatter soapFormat = new SoapFormatter();//创建二进制序列化器 //soapFormat.Serialize(fStream, list);//SOAP不能序列化泛型对象 soapFormat.Serialize(fStream, pList.ToArray()); } using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite)) { SoapFormatter soapFormat = new SoapFormatter();//创建二进制序列化器 //使用二进制反序列化对象 fStream.Position = 0;//重置流位置 List<Programmer> pList = ((Programmer[])soapFormat.Deserialize(fStream)).ToList();//反序列化对象 } }
XML序列化:
public static void XmlSerialize() { //使用XML序列化对象 string fileName = Path.Combine(CurrentDataPath, @"Student.xml");//文件名称与路径 using (Stream fStream = new FileStream(fileName, FileMode.Create, FileAccess.ReadWrite)) { List<Programmer> pList = DataFactory.BuildProgrammerList(); XmlSerializer xmlFormat = new XmlSerializer(typeof(List<Programmer>));//创建XML序列化器,需要指定对象的类型 xmlFormat.Serialize(fStream, pList); } using (Stream fStream = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite)) { XmlSerializer xmlFormat = new XmlSerializer(typeof(List<Programmer>));//创建XML序列化器,需要指定对象的类型 //使用XML反序列化对象 fStream.Position = 0;//重置流位置 List<Programmer> pList = pList = (List<Programmer>)xmlFormat.Deserialize(fStream); } }
总结:
1)使用二进制和SOAP时实体类必须添加[Serializable] 序列化特性,对类中不需要序列化的成员可以使用[NonSerialized]特性。
2)二进制的文件大小最小,而且使用二进制格式序列化时,它不仅是将对象的字段数据进行持久化,也持久化每个类型的完全限定名称和定义程序集的完整名称(包括包称、版本、公钥标记、区域性),这些数据使得在进行二进制格式反序列化时亦会进行类型检查。所以反序列化时的运行环境要与序列化时的运行环境要相同,否者可能会无法反序列化成功。
3)SOAP序列化与二进制序列化的区别是:SOAP序列化不能序列化泛型类型。与二进制序列化一样在序列化时不需要向序列化器指定序列化对象的类型。而XML序列化需要向XML序列化器指定序列化对象的类型。没记错的话像webservice传输数据是使用的就是SOAP
4)需要对XML序列化器指定需要序列化对象的类型和其关联的类型。XML序列化只能序列化对象的公有属性,并且要求对象有一个无参的构造方法,否者无法反序列化。[Serializable]和[NonSerialized]特性对XML序列化无效!所以使用XML序列化时不需要对对象增加[Serializable]特性。
***********************************************************************************************************************************************
最后再讲下json,使用json一般就两种,一种是.net框架中自带的JavaScriptSerializer,还有一种是使用第三方插件,Newtonsoft.Json,目前主流还是Newtonsoft.Json,而且据说有人测试过第三方插件的效率比框架自带的效率还要高。
public class JsonHelper { #region Json /// <summary> /// JavaScriptSerializer /// </summary> /// <typeparam name="T"></typeparam> /// <param name="obj"></param> /// <returns></returns> public static string ObjectToString<T>(T obj) { JavaScriptSerializer jss = new JavaScriptSerializer(); return jss.Serialize(obj); } /// <summary> /// JavaScriptSerializer /// </summary> /// <typeparam name="T"></typeparam> /// <param name="content"></param> /// <returns></returns> public static T StringToObject<T>(string content) { JavaScriptSerializer jss = new JavaScriptSerializer(); return jss.Deserialize<T>(content); } #region Newtonsoft.Json /// <summary> /// JsonConvert.SerializeObject /// </summary> /// <typeparam name="T"></typeparam> /// <param name="obj"></param> /// <returns></returns> public static string ToJson<T>(T obj) { return JsonConvert.SerializeObject(obj); } /// <summary> /// JsonConvert.DeserializeObject /// </summary> /// <typeparam name="T"></typeparam> /// <param name="content"></param> /// <returns></returns> public static T ToObject<T>(string content) { return JsonConvert.DeserializeObject<T>(content); } #endregion #endregion Json }
直接调用
public static void Json() { List<Programmer> pList = DataFactory.BuildProgrammerList(); string result = JsonHelper.ObjectToString<List<Programmer>>(pList); List<Programmer> pList1 = JsonHelper.StringToObject<List<Programmer>>(result); }
一般来讲,和前端交互比较多的一般都是使用json,因为前端解析xml还是比较麻烦的。反正各有各的好处吧,这就要看每个人怎么使用了。
最后附带一个验证码、图片缩放代码
1 public class ImageHelper 2 { 3 private static string ImagePath = System.Configuration.ConfigurationManager.AppSettings["ImagePath"]; 4 private static string VerifyPath = System.Configuration.ConfigurationManager.AppSettings["ImagePath"]; 5 //绘图的原理很简单:Bitmap就像一张画布,Graphics如同画图的手,把Pen或Brush等绘图对象画在Bitmap这张画布上 6 7 /// <summary> 8 /// 画验证码 9 /// </summary> 10 public static void Drawing() 11 { 12 Bitmap bitmapobj = new Bitmap(100, 100); 13 //在Bitmap上创建一个新的Graphics对象 14 Graphics g = Graphics.FromImage(bitmapobj); 15 //创建绘画对象,如Pen,Brush等 16 Pen redPen = new Pen(Color.Red, 8); 17 g.Clear(Color.White); 18 //绘制图形 19 g.DrawLine(redPen, 50, 20, 500, 20); 20 g.DrawEllipse(Pens.Black, new Rectangle(0, 0, 200, 100));//画椭圆 21 g.DrawArc(Pens.Black, new Rectangle(0, 0, 100, 100), 60, 180);//画弧线 22 g.DrawLine(Pens.Black, 10, 10, 100, 100);//画直线 23 g.DrawRectangle(Pens.Black, new Rectangle(0, 0, 100, 200));//画矩形 24 g.DrawString("我爱北京天安门", new Font("微软雅黑", 12), new SolidBrush(Color.Red), new PointF(10, 10));//画字符串 25 //g.DrawImage( 26 27 if (!Directory.Exists(ImagePath)) 28 Directory.CreateDirectory(ImagePath); 29 30 bitmapobj.Save(ImagePath + "pic1.jpg", ImageFormat.Jpeg); 31 //释放所有对象 32 bitmapobj.Dispose(); 33 g.Dispose(); 34 } 35 36 public static void VerificationCode() 37 { 38 Bitmap bitmapobj = new Bitmap(300, 300); 39 //在Bitmap上创建一个新的Graphics对象 40 Graphics g = Graphics.FromImage(bitmapobj); 41 g.DrawRectangle(Pens.Black, new Rectangle(0, 0, 150, 50));//画矩形 42 g.FillRectangle(Brushes.White, new Rectangle(1, 1, 149, 49)); 43 g.DrawArc(Pens.Blue, new Rectangle(10, 10, 140, 10), 150, 90);//干扰线 44 string[] arrStr = new string[] { "我", "们", "孝", "行", "白", "到", "国", "中", "来", "真" }; 45 Random r = new Random(); 46 int i; 47 for (int j = 0; j < 4; j++) 48 { 49 i = r.Next(10); 50 g.DrawString(arrStr[i], new Font("微软雅黑", 15), Brushes.Red, new PointF(j * 30, 10)); 51 } 52 bitmapobj.Save(Path.Combine(VerifyPath, "Verif.jpg"), ImageFormat.Jpeg); 53 bitmapobj.Dispose(); 54 g.Dispose(); 55 } 56 57 /// <summary> 58 /// 按比例缩放,图片不会变形,会优先满足原图和最大长宽比例最高的一项 59 /// </summary> 60 /// <param name="oldPath"></param> 61 /// <param name="newPath"></param> 62 /// <param name="maxWidth"></param> 63 /// <param name="maxHeight"></param> 64 public static void CompressPercent(string oldPath, string newPath, int maxWidth, int maxHeight) 65 { 66 System.Drawing.Image _sourceImg = System.Drawing.Image.FromFile(oldPath); 67 double _newW = (double)maxWidth; 68 double _newH = (double)maxHeight; 69 double percentWidth = (double)_sourceImg.Width > maxWidth ? (double)maxWidth : (double)_sourceImg.Width; 70 71 if ((double)_sourceImg.Height * (double)percentWidth / (double)_sourceImg.Width > (double)maxHeight) 72 { 73 _newH = (double)maxHeight; 74 _newW = (double)maxHeight / (double)_sourceImg.Height * (double)_sourceImg.Width; 75 } 76 else 77 { 78 _newW = percentWidth; 79 _newH = (percentWidth / (double)_sourceImg.Width) * (double)_sourceImg.Height; 80 } 81 System.Drawing.Image bitmap = new System.Drawing.Bitmap((int)_newW, (int)_newH); 82 Graphics g = Graphics.FromImage(bitmap); 83 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; 84 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 85 g.Clear(Color.Transparent); 86 g.DrawImage(_sourceImg, new Rectangle(0, 0, (int)_newW, (int)_newH), new Rectangle(0, 0, _sourceImg.Width, _sourceImg.Height), GraphicsUnit.Pixel); 87 _sourceImg.Dispose(); 88 g.Dispose(); 89 bitmap.Save(newPath, System.Drawing.Imaging.ImageFormat.Jpeg); 90 bitmap.Dispose(); 91 } 92 93 /// <summary> 94 /// 按照指定大小对图片进行缩放,可能会图片变形 95 /// </summary> 96 /// <param name="oldPath"></param> 97 /// <param name="newPath"></param> 98 /// <param name="newWidth"></param> 99 /// <param name="newHeight"></param> 100 public static void ImageChangeBySize(string oldPath, string newPath, int newWidth, int newHeight) 101 { 102 Image sourceImg = Image.FromFile(oldPath); 103 System.Drawing.Image bitmap = new System.Drawing.Bitmap(newWidth, newHeight); 104 Graphics g = Graphics.FromImage(bitmap); 105 g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High; 106 g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality; 107 g.Clear(Color.Transparent); 108 g.DrawImage(sourceImg, new Rectangle(0, 0, newWidth, newHeight), new Rectangle(0, 0, sourceImg.Width, sourceImg.Height), GraphicsUnit.Pixel); 109 sourceImg.Dispose(); 110 g.Dispose(); 111 bitmap.Save(newPath, System.Drawing.Imaging.ImageFormat.Jpeg); 112 bitmap.Dispose(); 113 } 114 }
IOSerialize(序列化)