• C#_.net core 3.0自定义读取.csv文件数据_解决首行不是标题的问题_Linqtocsv改进


      linqtocsv文件有不太好的地方就是:无法设置标题的行数,默认首行就是标题,这不是很尴尬吗?   并不是所有的csv文件严格写的首行是标题,下面全是数据,我接受的任务就是读取很多.csv报表数据,里面就有很多前几行是说明性内容,下面才是标题和数据。为了更好的解决这个问题,自己写吧...

      本博客没有照搬linqtocsv全部源码,保留了主要功能,并对其优化,为我所用,哈哈...

      

      下面是主要代码:

      1-主文件CsvHelper:

      这里在独自解析数据的时候,遇到了很多坑:

      a-遇到数据含有分隔符的问题的解决办法,代码已经包含了

      b-遇到了解析源文档数据时,未指定字符编码时,部分数据丢失导致csv文件个别行数据解析异常的问题,针对该问题,就是老老实实把读取文件时加了字符编码的参数进去,默认UTF-8。  

    using Microsoft.Extensions.Logging;
    using PaymentAccountAPI.Helper;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    
    namespace PaymentAccountAPI.CSV
    {
        public class CsvHelper
        {
            /// <summary>
            /// 日志
            /// </summary>
            private ILogger _Logger { get; set; }
    
            public CsvHelper(ILogger<CsvHelper> logger)
            {
                this._Logger = logger;
            }
    
            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 - 1];
                                        if (!string.IsNullOrEmpty(propertyValue))
                                        {
                                            try
                                            {
                                                if (isExistSplitChart && propertyValue.Contains("{分隔符}"))
                                                {
                                                    propertyValue = propertyValue.Replace("{分隔符}", fileDescription.SeparatorChar.ToString());
                                                }
    
                                                TypeHelper.SetPropertyValue(t, propertyInfo.Name, propertyValue);
                                            }
                                            catch (Exception e)
                                            {
                                                this._Logger.LogWarning(e, $"第{currentRawIndex + 1}行数据{propertyValue}转换属性{propertyInfo.Name}-{propertyInfo.PropertyType.Name}失败!");
                                                continue;
                                            }
                                        }
                                    }
                                    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.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)
                                {
                                    this._Logger.LogInformation($"已写入文件:{path},数据量:{currentRawIndex}");
                                }
    #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 - 1] = 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 - 1] = formatValue;
                                            }
                                        }
                                    }
                                    rawValue = string.Join(fileDescription.SeparatorChar, rawValueArray);
                                }
                                rawValueBuilder.Append(rawValue + "
    ");
                            }
                            catch (Exception e)
                            {
                                this._Logger.LogWarning(e, $"(异常)Excel第{currentRawIndex+1}行,数据列表第{tIndex + 1}个数据写入失败!rawValue:{rawValue}");
                                throw;
                            }
    
                            currentRawIndex++;
                        } while (tIndex < dataCount);
                        streamWriter.Write(rawValueBuilder.ToString());
    
                        streamWriter.Close();
                        streamWriter.Dispose();
                    }
                }
            }
    
        }
    }

      2-CSV映射类特性:

      

    using System;
    
    namespace PaymentAccountAPI.CSV
    {
        /// <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;
            }
        }
    }

      3-CSV文件描述信息类:

      

    using System.Text;
    
    namespace PaymentAccountAPI.CSV
    {
        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>
            /// 分隔符(默认为(,),也可以是其他分隔符如(	))
            /// </summary>
            public char SeparatorChar { get; set; }
            /// <summary>
            /// 标题所在行位置(默认为0,没有标题填-1)
            /// </summary>
            public int TitleRawIndex { get; set; }
    
        }
    }

      4-映射类获取关系帮助类:

      

    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    
    namespace PaymentAccountAPI.CSV
    {
        /// <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;
            }
    
        }
    
    }

      5-其他扩展类:

      

    namespace PaymentAccountAPI.Helper
    {
        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;
            }
        }
    }

      最后就是将CsvHelper注入到单例中,就可以使用了...

  • 相关阅读:
    中文乱码总结之web乱码情景
    微信小程序实现navbar导航栏
    boostrap table接收到后台返回的数据格式不一致的解决方法
    bootstrap让footer固定在顶部和底部
    在vue中让某个组件重新渲染的笨方法
    网页打印事件的监听
    关于JavaScript的词法作用域及变量提升的个人理解
    函数节流之debounce
    HTML5 a标签的down属性进行图片下载
    Jquery的深浅拷贝涉及到的知识点
  • 原文地址:https://www.cnblogs.com/lxhbky/p/12219080.html
Copyright © 2020-2023  润新知