来源:https://www.cnblogs.com/lxhbky/p/12219080.html
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; namespace StockCheckApp.Utils { public sealed class CsvHelper { private static readonly CsvHelper instance = new CsvHelper(); public static CsvHelper getInstance() { return instance; } /// <summary> /// 日志 /// </summary> //private ILogger _Logger { get; set; } //public CsvHelper(ILogger<CsvHelper> logger) //{ // this._Logger = logger; //} private CsvHelper() { } public List<T> Read<T>(string filePath, CsvFileDescription fileDescription) where T : class, new() { List<T> tList = new List<T>(50 * 10000); T t = null; int currentRawIndex = 0; if (File.Exists(filePath)) { using (StreamReader streamReader = new StreamReader(filePath, fileDescription.Encoding)) { Dictionary<int, FieldMapper> fieldMapperDic = FieldMapper.GetModelFieldMapper<T>().ToDictionary(m => m.CSVTitleIndex); string rawValue = null; string[] rawValueArray = null; PropertyInfo propertyInfo = null; string propertyValue = null; bool rawReadEnd = false; bool isExistSplitChart = false; do { rawValue = streamReader.ReadLine(); //标题行 if (currentRawIndex > fileDescription.TitleRawIndex) { if (!string.IsNullOrEmpty(rawValue)) { //替换字符串含有分隔符为{分隔符},最后再替换回来 if (rawValue.Contains("\"")) { isExistSplitChart = true; int yhBeginIndex = 0; int yhEndIndex = 0; string yhText = null; do { yhBeginIndex = StringHelper.GetIndexOfStr(rawValue, "\"", 1); yhEndIndex = StringHelper.GetIndexOfStr(rawValue, "\"", 2); yhText = rawValue.Substring(yhBeginIndex, (yhEndIndex - yhBeginIndex + 1)); string newYHText = yhText.Replace("\"", "").Replace(fileDescription.SeparatorChar.ToString(), "{分隔符}"); rawValue = rawValue.Replace(yhText, newYHText); } while (rawValue.Contains("\"")); } rawValueArray = rawValue.Split(fileDescription.SeparatorChar); t = new T(); foreach (var fieldMapper in fieldMapperDic) { propertyInfo = fieldMapper.Value.PropertyInfo; propertyValue = rawValueArray[fieldMapper.Key]; if (!string.IsNullOrEmpty(propertyValue)) { try { if (isExistSplitChart && propertyValue.Contains("{分隔符}")) { propertyValue = propertyValue.Replace("{分隔符}", fileDescription.SeparatorChar.ToString()); } TypeHelper.SetPropertyValue(t, propertyInfo.Name, propertyValue); } catch (Exception e) { string msg = $"第{currentRawIndex + 1}行数据{propertyValue}转换属性{propertyInfo.Name}-{propertyInfo.PropertyType.Name}失败!"; //this._Logger.LogWarning(e, msg); //continue; throw new Exception(msg); } } } tList.Add(t); } else { rawReadEnd = true; } } currentRawIndex++; } while (rawReadEnd == false); } } return tList; } public void WriteFile<T>(string path, List<T> tList, CsvFileDescription fileDescription) where T : class, new() { if (!string.IsNullOrEmpty(path)) { string fileDirectoryPath = null; if (path.Contains("\\")) { fileDirectoryPath = path.Substring(0, path.LastIndexOf('\\')); } else { fileDirectoryPath = path.Substring(0, path.LastIndexOf('/')); } if (!Directory.Exists(fileDirectoryPath)) { Directory.CreateDirectory(fileDirectoryPath); } int dataCount = tList.Count; Dictionary<int, FieldMapper> fieldMapperDic = FieldMapper.GetModelFieldMapper<T>().ToDictionary(m => m.CSVTitleIndex); int titleCount = fieldMapperDic.Keys.Count;//.Max(); string[] rawValueArray = new string[titleCount]; StringBuilder rawValueBuilder = new StringBuilder(); string rawValue = null; T t = null; PropertyInfo propertyInfo = null; int currentRawIndex = 0; int tIndex = 0; using (StreamWriter streamWriter = new StreamWriter(path, false, fileDescription.Encoding)) { do { try { rawValue = ""; #if DEBUG if (currentRawIndex % 10000 == 0) { string msg = $"已写入文件:{path},数据量:{currentRawIndex}"; //this._Logger.LogInformation(msg); } #endif if (currentRawIndex >= fileDescription.TitleRawIndex) { //清空数组数据 for (int i = 0; i < titleCount; i++) { rawValueArray[i] = ""; } if (currentRawIndex > fileDescription.TitleRawIndex) { t = tList[tIndex]; tIndex++; } foreach (var fieldMapperItem in fieldMapperDic) { //写入标题行 if (currentRawIndex == fileDescription.TitleRawIndex) { rawValueArray[fieldMapperItem.Key] = fieldMapperItem.Value.CSVTitle; } //真正的数据从标题行下一行开始写 else { propertyInfo = fieldMapperItem.Value.PropertyInfo; object propertyValue = propertyInfo.GetValue(t); string formatValue = null; if (propertyValue != null) { if (propertyInfo.PropertyType is IFormattable && !string.IsNullOrEmpty(fieldMapperItem.Value.OutputFormat)) { formatValue = ((IFormattable)propertyValue).ToString(fieldMapperItem.Value.OutputFormat, null); } else { formatValue = propertyValue.ToString(); } //如果属性值含有分隔符,则使用双引号包裹 if (formatValue.Contains(fileDescription.SeparatorChar.ToString())) { formatValue = $"\"{formatValue}\""; } rawValueArray[fieldMapperItem.Key] = formatValue; } } } rawValue = string.Join(fileDescription.SeparatorChar, rawValueArray); } rawValueBuilder.Append(rawValue + "\r\n"); } catch (Exception e) { string msg = $"(异常)Excel第{currentRawIndex + 1}行,数据列表第{tIndex + 1}个数据写入失败!rawValue:{rawValue}"; //this._Logger.LogWarning(e, msg); throw new Exception(msg); } currentRawIndex++; } while (tIndex < dataCount); streamWriter.Write(rawValueBuilder.ToString()); streamWriter.Close(); streamWriter.Dispose(); } } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; namespace StockCheckApp.Utils { /// <summary> /// 字段映射类 /// </summary> public class FieldMapper { /// <summary> /// 属性信息 /// </summary> public PropertyInfo PropertyInfo { get; set; } /// <summary> /// 标题 /// </summary> public string CSVTitle { get; set; } /// <summary> /// 标题下标位置 /// </summary> public int CSVTitleIndex { get; set; } /// <summary> /// 字符输出格式(数字和日期类型需要) /// </summary> public string OutputFormat { get; set; } public static List<FieldMapper> GetModelFieldMapper<T>() { List<FieldMapper> fieldMapperList = new List<FieldMapper>(100); List<PropertyInfo> tPropertyInfoList = typeof(T).GetProperties().ToList(); CsvColumnAttribute csvColumnAttribute = null; foreach (var tPropertyInfo in tPropertyInfoList) { csvColumnAttribute = (CsvColumnAttribute)tPropertyInfo.GetCustomAttribute(typeof(CsvColumnAttribute)); if (csvColumnAttribute != null) { fieldMapperList.Add(new FieldMapper { PropertyInfo = tPropertyInfo, CSVTitle = csvColumnAttribute.Title, CSVTitleIndex = csvColumnAttribute.TitleIndex, OutputFormat = csvColumnAttribute.OutputFormat }); } } return fieldMapperList; } } } using System; using System.Collections.Generic; using System.Text; namespace StockCheckApp.Utils { public class StringHelper { /// <summary> /// 获取字符串中第strPosition个位置的str的下标 /// </summary> /// <param name="text"></param> /// <param name="str"></param> /// <param name="strPosition"></param> /// <returns></returns> public static int GetIndexOfStr(string text, string str, int strPosition) { int strIndex = -1; int currentPosition = 0; if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(str) && strPosition >= 1) { do { currentPosition++; if (strIndex == -1) { strIndex = text.IndexOf(str); } else { strIndex = text.IndexOf(str, strIndex + 1); } } while (currentPosition < strPosition); } return strIndex; } } } using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; namespace StockCheckApp.Utils { /// <summary> /// 类型帮助类 /// </summary> public class TypeHelper { private static Dictionary<Type, List<PropertyInfo>> _TypePropertyDic = new Dictionary<Type, List<PropertyInfo>>(0); /// <summary> /// 获取T属性列表 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public static List<PropertyInfo> GetTPropertyDic<T>() { Type tType = typeof(T); List<PropertyInfo> propertyInfoList = null; if (TypeHelper._TypePropertyDic.ContainsKey(tType)) { propertyInfoList = TypeHelper._TypePropertyDic[tType]; } else { propertyInfoList = tType.GetProperties().ToList(); TypeHelper._TypePropertyDic.Add(tType, propertyInfoList); } return propertyInfoList; } /// <summary> /// 获取t属性值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="propertyName"></param> /// <returns></returns> public static object GetPropertyValue<T>(T t, string propertyName) { object value = null; Type tType = typeof(T); List<PropertyInfo> propertyInfoList = TypeHelper.GetTPropertyDic<T>(); PropertyInfo propertyInfo = propertyInfoList.FirstOrDefault(m => m.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); if (propertyInfo != null) { value = propertyInfo.GetValue(t); } return value; } /// <summary> /// 设置t属性值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="t"></param> /// <param name="propertyName"></param> /// <param name="propertyValue"></param> public static void SetPropertyValue<T>(T t, string propertyName, object propertyValue) { Type tType = typeof(T); List<PropertyInfo> propertyInfoList = TypeHelper.GetTPropertyDic<T>(); PropertyInfo propertyInfo = propertyInfoList.FirstOrDefault(m => m.Name.Equals(propertyName, StringComparison.OrdinalIgnoreCase)); if (propertyInfo != null && propertyValue != null) { if (!propertyInfo.PropertyType.IsGenericType) { propertyInfo.SetValue(t, Convert.ChangeType(propertyValue, propertyInfo.PropertyType)); } else { Type genericTypeDefinition = propertyInfo.PropertyType.GetGenericTypeDefinition(); if (genericTypeDefinition == typeof(Nullable<>)) { propertyInfo.SetValue(t, Convert.ChangeType(propertyValue, Nullable.GetUnderlyingType(propertyInfo.PropertyType))); } } } } /// <summary> /// 克隆相同属性数据到另一个类列表中 /// </summary> /// <typeparam name="Tin"></typeparam> /// <typeparam name="Tout"></typeparam> /// <param name="inList"></param> /// <returns></returns> public static List<Tout> CloneData<Tin, Tout>(List<Tin> inList) where Tout : new() { List<Tout> outList = new List<Tout>(0); if (inList != null && inList.Count > 0) { Dictionary<string, PropertyInfo> inTProList = TypeHelper.GetTPropertyDic<Tin>().ToDictionary(m => m.Name); Dictionary<string, PropertyInfo> outTProList = TypeHelper.GetTPropertyDic<Tout>().ToDictionary(m => m.Name); Tout outT = default(Tout); foreach (var item in inList) { outT = new Tout(); foreach (var inProItem in inTProList) { if (outTProList.ContainsKey(inProItem.Key)) { outTProList[inProItem.Key].SetValue(outT, inProItem.Value.GetValue(item)); } } outList.Add(outT); } } return outList; } } } using System; using System.Collections.Generic; using System.Text; namespace StockCheckApp.Utils { /// <summary> /// Csv文件类特性标记 /// </summary> [System.AttributeUsage(System.AttributeTargets.Field | System.AttributeTargets.Property, AllowMultiple = false)] public class CsvColumnAttribute : System.Attribute { internal const int defaultTitleIndex = Int32.MaxValue; /// <summary> /// 标题 /// </summary> public string Title { get; set; } /// <summary> /// 标题位置(从0开始) /// </summary> public int TitleIndex { get; set; } /// <summary> /// 字符输出格式(数字和日期类型需要) /// </summary> public string OutputFormat { get; set; } public CsvColumnAttribute() { Title = ""; TitleIndex = defaultTitleIndex; OutputFormat = ""; } public CsvColumnAttribute(string title, int titleIndex, string outputFormat) { Title = title; TitleIndex = titleIndex; OutputFormat = outputFormat; } } } using System; using System.Collections.Generic; using System.Text; namespace StockCheckApp.Utils { public class CsvFileDescription { public CsvFileDescription() : this(0) { } public CsvFileDescription(int titleRawIndex) : this(',', titleRawIndex, Encoding.UTF8) { } public CsvFileDescription(char separatorChar, int titleRawIndex, Encoding encoding) { this.SeparatorChar = separatorChar; this.TitleRawIndex = titleRawIndex; this.Encoding = encoding; } /// <summary> /// CSV文件字符编码 /// </summary> public Encoding Encoding { get; set; } /// <summary> /// 分隔符(默认为(,),也可以是其他分隔符如(\t)) /// </summary> public char SeparatorChar { get; set; } /// <summary> /// 标题所在行位置(默认为0,没有标题填-1) /// </summary> public int TitleRawIndex { get; set; } } }
using StockCheckApp.Utils; using System; using System.Collections.Generic; using System.Text; using System.Xml.Linq; namespace StockCheckApp.Models { public class GoodsCheckInfo { /// <summary> /// 指示当前对象自创建以来,属性 GoodsCode 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isGoodsCodeSetValue; private string _goodsCode; /// <summary> /// 商品代码 /// </summary> [CsvColumn(Title = "商品代码", TitleIndex = 0, OutputFormat = "")] public string GoodsCode { get { return this._goodsCode; } set { this._goodsCode = value; this._isGoodsCodeSetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 LotNo 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isLotNoSetValue; private string _lotNo; /// <summary> /// 批号 /// </summary> [CsvColumn(Title = "批号", TitleIndex = 1, OutputFormat = "")] public string LotNo { get { return this._lotNo; } set { this._lotNo = value; this._isLotNoSetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 GoodsQty 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isGoodsQtySetValue; private string _goodsQty; /// <summary> /// 盘点数量 /// </summary> [CsvColumn(Title = "盘点数量", TitleIndex = 2, OutputFormat = "")] public string GoodsQty { get { return this._goodsQty; } set { this._goodsQty = value; this._isGoodsQtySetValue = true; } } public bool UpdateTag { get; set; } } } using StockCheckApp.Utils; using System; using System.Collections.Generic; using System.Text; using System.Xml.Linq; namespace StockCheckApp.Models { public class GoodsInfo { /// <summary> /// 指示当前对象自创建以来,属性 GoodsCode 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isGoodsCodeSetValue; private string _goodsCode; /// <summary> /// 商品编号 /// </summary> [CsvColumn(Title = "商品代码", TitleIndex = 0, OutputFormat = "")] public string GoodsCode { get { return this._goodsCode; } set { this._goodsCode = value; this._isGoodsCodeSetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 BarCode1 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isBarCode1SetValue; private string _barCode1; /// <summary> /// 商品条码1 /// </summary> [CsvColumn(Title = "条码1", TitleIndex = 1, OutputFormat = "")] public string BarCode1 { get { return this._barCode1; } set { this._barCode1 = value; this._isBarCode1SetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 BarCode2 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isBarCode2SetValue; private string _barCode2; /// <summary> /// 商品条码2 /// </summary> [CsvColumn(Title = "条码2", TitleIndex = 2, OutputFormat = "")] public string BarCode2 { get { return this._barCode2; } set { this._barCode2 = value; this._isBarCode2SetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 BarCode3 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isBarCode3SetValue; private string _barCode3; /// <summary> /// 商品条码3 /// </summary> [CsvColumn(Title = "条码3", TitleIndex = 3, OutputFormat = "")] public string BarCode3 { get { return this._barCode3; } set { this._barCode3 = value; this._isBarCode3SetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 BarCode4 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isBarCode4SetValue; private string _barCode4; /// <summary> /// 商品条码4 /// </summary> [CsvColumn(Title = "条码4", TitleIndex = 4, OutputFormat = "")] public string BarCode4 { get { return this._barCode4; } set { this._barCode4 = value; this._isBarCode4SetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 BarCode5 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isBarCode5SetValue; private string _barCode5; /// <summary> /// 商品条码5 /// </summary> [CsvColumn(Title = "条码5", TitleIndex = 5, OutputFormat = "")] public string BarCode5 { get { return this._barCode5; } set { this._barCode5 = value; this._isBarCode5SetValue = true; } } /// <summary> /// 指示当前对象自创建以来,属性 GoodsName 是否已经设置了值(含设置为 null)。 /// </summary> protected bool _isGoodsNameSetValue; private string _goodsName; /// <summary> /// 商品名称(中文) /// </summary> [CsvColumn(Title = "商品名称", TitleIndex = 6, OutputFormat = "")] public string GoodsName { get { return this._goodsName; } set { this._goodsName = value; this._isGoodsNameSetValue = true; } } } }
使用:
string dirGoodsMst = Directory.GetCurrentDirectory() + "\\DataResource\\GoodsMst.csv";
List<GoodsInfo> GoodsInfos = CsvHelper.getInstance().Read<GoodsInfo>(dirGoodsMst, csvFileDescription);