Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 628|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-6-20 18:16:43 | 显示全部楼层 |阅读模式

      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 = "";
                                    }
    
                                    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 + "\r\n");
                            }
                            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>
            /// 分隔符(默认为(,),也可以是其他分隔符如(\t))
            /// </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注入到单例中,就可以使用了...

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-4-19 11:03 , Processed in 0.088000 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表