一个好的程序对于日志的处理是必不可少的。好的日志记录可以帮助我们减少更好的查找错误和系统的维护。今天整理一下自己工作中平时用来记录日志的类,同时也补补基础知识。
功能: 根据程序App.config中配置的路径,创建日志文件并将程序的日志写到相应的文件中。
首先来看一下我之前自己写的一个用于写日志的类,源代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Configuration; namespace LogHelp { public class LogHelp { private string _filename; private static Dictionary<long, long> lockDic = new Dictionary<long, long>(); //获取或设置文件名称 public string FileName { get { return _filename; } set { _filename = value; } } //构造函数;根据传入的文件名,将日志写到相应的路径下的文件中。 public LogHelp(string filename) { string folder = ConfigurationManager.AppSettings["LogPath"]+"\"+System.DateTime.Now.Date.ToString("yyyyMMdd"); if (!System.IO.Directory.Exists(folder)) System.IO.Directory.CreateDirectory(folder); if (!filename.ToLower().EndsWith(".txt")) { filename = filename.Split('.')[0]; filename += ".txt"; } _filename = folder + "\" + filename; } //创建文件 public void Create(string fileName) { if (!System.IO.File.Exists(fileName)) { using (System.IO.FileStream fs= System.IO.File.Create(fileName)) { fs.Close(); } } } //写入文本 private void Write(string content, string newLine) { if (string.IsNullOrEmpty(_filename)) { throw new Exception("FileName不能为空!"); } using(System.IO.FileStream fs=new System.IO.FileStream(_filename,System.IO.FileMode.OpenOrCreate,System.IO.FileAccess.ReadWrite,System.IO.FileShare.ReadWrite,8,System.IO.FileOptions.Asynchronous)) { //FileStream只能处理字节,须通过编码将字符数据转换成字节。 //新建字节型数组dataArrary对象,dataArrary对象得到了content+newLine的Encoding的值 Byte[] dataArrary = System.Text.Encoding .Default.GetBytes(content+newLine); bool flag = true; long slen = dataArrary.Length; long len = 0; while (flag) { try { if (len >= fs.Length) { fs.Lock(len, slen); lockDic[len] = slen; flag = false; } else { len = fs.Length; } } catch (Exception ex) { while (!lockDic.ContainsKey(len)) { len += lockDic[len]; } } } fs.Seek(len, System.IO.SeekOrigin.Begin); //seek设置文件的读取和写入位置 fs.Write(dataArrary, 0, dataArrary.Length); fs.Close(); } } //写入文件内容 public void WriteLine(string content) { this.Write(content, System.Environment.NewLine); } //写入文件 public void Write(string content) { this.Write(content, ""); } } }
这个类基本就可以实现日志的记录啦,当然都是很基础的功能哈。自己在住程序中创建实体类,直接调用即可啦。
比如我在App.config文件中配置了路径: "D:studylog", 在主程序中调用如下:
public class Program { private static LogHelp log = new LogHelp("LogTest"); static void Main(string[] args) { log.WriteLine("Hello World"); } }
直接运行程序,你就可以在D:studylogyyyymmddlogtest.txt 文件中看到写入的”Hello World"啦。就这么简单,没什么可说的啦。
好啦,接下来,我们来看一下日志类中用到的Dictionary 和FileStream 用法的一些总结,加深了解,多了解没坏处啦。
Dictionary的用法总结:
需要引入命名空间: System.Collections.Generic (程序集:mscorlib)
Dictionary<string,string>是一个泛型,他本身有集合的功能有时可以把它看成数组;
他的结构是这样的: Dictionary<[key],[value]>,
他的特点是存入对象是需要与[key]值一一对应的存入该泛型;
1. 用法一、常规用
增加键值对之前需要判断是否存在该键,如果已经存在该键而且不判断,将抛出异常。所以这样每次都要进行判断,很麻烦,在备注使用了一个扩展方法:
构建一个Dictionary: Dictionary<string, string> plist = new Dictionary<string,string>;
读取Dictionary中的Key和Value,判断是否包含某个Key: plist.ContainsKey("");
遍历Key: foreach(var key in plist.keys);
遍历Value: foreach(string value in plist.values);
遍历Key和Value: foreach(var dic in plist) { dic.key,dic.value};
2. 用法二、Dictionary的value为一个数组,代码示例如下:
public static void Sample() { Dictionary<string,string[]> dic = new Dictionary<string,string[]>(); string[] zhejiang = {"aa","bb","cc"}; string[] shanghai = {“pudong","waitan"}; dic.Add("ZJ", zhejiang); dic.Add("SH",shanghai); }
3. 用法三、Dictionary的Value为一个类;
public static void Sample3() { Dictionary<string, string> stuList = new Dictionary<string,string>(); Student stu=null; for(int i=0; i<3;i++) { stu = new Student(); stu.Num = i.ToString(); stu.Name = "StuName"+i.ToString(); stuList.Add(i.ToString(),stu); } }
备注: Dictionary的扩展方法使用:
public static void DicSample4() { //1)普通调用 Dictionary<int, String> dict = new Dictionary<int, String>(); DictionaryExtensionMethodClass.TryAdd(dict, 1, "ZhangSan"); DictionaryExtensionMethodClass.TryAdd(dict, 2, "WangWu"); DictionaryExtensionMethodClass.AddOrPeplace(dict, 3, "WangWu"); DictionaryExtensionMethodClass.AddOrPeplace(dict, 3, "ZhangWu"); DictionaryExtensionMethodClass.TryAdd(dict, 2, "LiSi"); //2)TryAdd 和 AddOrReplace 这两个方法具有较强自我描述能力,用起来很省心,而且也简单: dict.AddOrPeplace(20, "Orange"); dict.TryAdd(21, "Banana"); dict.TryAdd(22, "apple"); //3)像Linq或jQuery一样连起来写 dict.TryAdd(10, "Bob") .TryAdd(11, "Tom") .AddOrPeplace(12, "Jom"); //4) 获取值 String F = "Ba"; dict.TryGetValue(31, out F); Console.WriteLine("F : {0}",F); foreach (var dic in dict) { Console.WriteLine("Output : Key : {0}, Value : {1}", dic.Key, dic.Value); } //5)下面是使用GetValue获取值 var v1 = dict.GetValue(111,null); var v2 = dict.GetValue(10,"abc"); //6)批量添加 var dict1 = new Dictionary<int,int>(); dict1.AddOrPeplace(3, 3); dict1.AddOrPeplace(5, 5); var dict2 = new Dictionary<int, int>(); dict2.AddOrPeplace(1, 1); dict2.AddOrPeplace(4, 4); dict2.AddRange(dict1, false); } 扩展方法所在的类 public static class DictionaryExtensionMethodClass { /// <summary> /// 尝试将键和值添加到字典中:如果不存在,才添加;存在,不添加也不抛导常 /// </summary> public static Dictionary<TKey, TValue> TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value) { if (dict.ContainsKey(key) == false) dict.Add(key, value); return dict; } /// <summary> /// 将键和值添加或替换到字典中:如果不存在,则添加;存在,则替换 /// </summary> public static Dictionary<TKey, TValue> AddOrPeplace<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue value) { dict[key] = value; return dict; } /// <summary> /// 获取与指定的键相关联的值,如果没有则返回输入的默认值 /// </summary> public static TValue GetValue<TKey, TValue>(this Dictionary<TKey, TValue> dict, TKey key, TValue defaultValue) { return dict.ContainsKey(key)?dict[key] : defaultValue; } /// <summary> /// 向字典中批量添加键值对 /// </summary> /// <param name="replaceExisted">如果已存在,是否替换</param> public static Dictionary<TKey, TValue> AddRange<TKey, TValue>(this Dictionary<TKey, TValue> dict, IEnumerable<KeyValuePair<TKey, TValue>> values, bool replaceExisted) { foreach (var item in values) { if (dict.ContainsKey(item.Key) == false || replaceExisted) dict[item.Key] = item.Value; } return dict; } }
其他常见的属性和方法的说明:
Comparer: 获取用于确定字段中键是否相等的IEqualityComparer;
Count: 获取包含在Dictionary中键值对的数目;
Item: 获取或设置与指定的键相关联的值;
Keys: 获取包含Dictionary中键的集合;
Values: 获取包含Dictionary中的值的集合;
Add: 将指定的键和值添加到字典中;
Clear: 从Dictionary中移除所有的键和值;
ContainsKey: 确定Dictionary是否包含指定的键;
ContainsValue: 确定Dictionary是否包含特定值;
GetEnumerator: 返回循环访问Dictionary的枚举值;
GetType: 获取当前实例的Type(从Object继承);
Remove: 从Dictionary中移除所指定的键的值;
ToString: 返回表示当前Object的String.(从Object继承);
TryGetValue: 获取与指定的键相关联的值。
FileStream用法总结:
引用命名空间: using System.IO
FileStream类只能处理原始字节(raw byte)。FileStream类可以用于任何数据文件,而不仅仅是文本文件。FileStream对象可以用于读取诸如图像和声音的文件,FileStream读取出来的是字节数组,然后通过编码转换将字节数组转换成字符串。
1. 读取文件:
第一步: 声明一个FileStream类的对象:
FileStream fsRead = new FileStream(string path, FileMode mode, FileAccess access);
参数:
path: 要操作文件的路径,路径可以是绝对路径或者相对路径;
mode: 操作文件的方式,打开或者创建;
access: 操作文件中的数据,读取或者写入。
第二步: 调用fsRead对象的方法Read;
下面方法是从文件中读取数据,再把数据写入一个字节数组;
FileStream.Read(byte[] array, int offset, int count);
参数:
array: 用了存储fsRead对象读取到数据的字节数组;
offset: 开始读取数据的位置,通常是0.
count: 最多读取的字节数。
2. 写入文件:
第一步: 声明一个FileStream类的对象:
FileStream fsWrite = new FileStream(string path, FileMode mode, FileAccess access);
第二步: 调用fsWrite对象的方法Write;
FileStream.Write(byte[] array, int offset, int count): 将字节数组数据写入到指定的文本。
FileStream常用的属性和方法:
属性:
CanRead 判断当前流是否支持读取,返回bool值,True表示可以读取
CanWrite 判断当前流是否支持写入,返回bool值,True表示可以写入
方法:
Read() 从流中读取数据,返回字节数组
Write() 将字节块(字节数组)写入该流
Seek() 设置文件读取或写入的起始位置
Flush() 清除该流缓冲区,使得所有缓冲的数据都被写入到文件中
Close() 关闭当前流并释放与之相关联的所有系统资源
文件的访问方式:(FileAccess)
包括三个枚举:
FileAccess.Read(对文件读访问)
FileAccess.Write(对文件进行写操作)
FileAccess.ReadWrite(对文件读或写操作)
文件打开模式:(FileMode)包括6个枚举
FileMode.Append 打开现有文件准备向文件追加数据,只能同FileAccess.Write一起使用
FileMode.Create 指示操作系统应创建新文件,如果文件已经存在,它将被覆盖
FileMode.CreateNew 指示操作系统应创建新文件,如果文件已经存在,将引发异常
FileMode.Open 指示操作系统应打开现有文件,打开的能力取决于FileAccess所指定的值
FileMode.OpenOrCreate 指示操作系统应打开文件,如果文件不存在则创建新文件
FileMode.Truncate 指示操作系统应打开现有文件,并且清空文件内容
文件共享方式:(FileShare)
FileShare方式是为了避免几个程序同时访问同一个文件会造成异常的情况。
文件共享方式包括四个:
FileShare.None 谢绝共享当前文件
FileShare.Read 充许别的程序读取当前文件
FileShare.Write 充许别的程序写当前文件
FileShare.ReadWrite 充许别的程序读写当前文件
使用FileStream类创建文件流对象:
FileStream(String 文件路径,FileMode 文件打开模式)
FileStream(String 文件路径,FileMode 文件打开模式,FileAccess 文件访问方式)
FileStream(String 文件路径,FileMode 文件打开模式,FileAccess 文件访问方式,FileShare 文件共享方式)
使用File类来创建对象:(常用)
自定义打开文件的方式:File.Open(String,FileMode);
打开文件进行读取: File.OpenRead(String);
打开文件进行写入: File.OpenWrite(String);
注:
对文件的读写操多不管代码有多少,无非就是下面的三步:
1.创建文件读写流对象
2.对文件进行读写
3.关闭文件流