来源: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);
/// <summary> /// 反序列化固定字符串格式 属性用\t间隔,行用\n间隔 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="data"></param> /// <returns></returns> public static List<T> DeserializeFromString<T>(string data, bool exceptHeader) { //类型T的所有属性 Dictionary<int, FieldMapper> fieldMapperDic = FieldMapper.GetModelFieldMapper<T>().ToDictionary(m => m.ColumnIndex); List<T> list = new(); var targetType = typeof(T); var dataString = data.Split(new string[] { "\n" }, StringSplitOptions.RemoveEmptyEntries); if (exceptHeader && dataString.Length > 0) { dataString = dataString.Remove(dataString[0]); } foreach (var item in dataString) { //创建新对象 T t = (T)Activator.CreateInstance(targetType); //对象值列表 var stockCheckItemStrArray = item.Split(new string[] { "\t" }, StringSplitOptions.None); //数据长度和字段长度比对,哪个短,以哪个为准。超出的不予赋值。避免报错。 int dataCount = stockCheckItemStrArray.Length; if (stockCheckItemStrArray.Length > fieldMapperDic.Count) { //throw new Exception("数据长度" + stockCheckItemStrArray.Length + ",超出字段长度" + fieldMapperDic.Count + ":" + item); dataCount = fieldMapperDic.Count; string msgWrite = "targetType:" + targetType + ",数据长度" + stockCheckItemStrArray.Length + ",超出字段长度" + fieldMapperDic.Count + ":" + item; AppTraceLog.WriteInfo(msgWrite); } //循环为对象属性赋值 for (int i = 0; i < dataCount; i++) { string proName = fieldMapperDic[i].PropertyInfo.Name; string proValue = stockCheckItemStrArray[i]; PropertyInfo pit = targetType.GetProperty(proName); pit.SetValue(t, proValue, null); } //保存到集合中 list.Add(t); } return list; } /// <summary> /// 反序列化固定字符串格式 属性用\t间隔,行用\n间隔 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="data"></param> /// <returns></returns> public static List<T> DeserializeFromCompressString<T>(string data, bool exceptHeader) { //解压缩字符串 string decompressStr = StringCompress.DecompressString(data); var res = DeserializeFromString<T>(decompressStr, exceptHeader); return res; }
浙公网安备 33010602011771号