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>( * ); T t = null;
int currentRawIndex = ; 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 = ;
int yhEndIndex = ;
string yhText = null;
do
{
yhBeginIndex = StringHelper.GetIndexOfStr(rawValue, "\"", );
yhEndIndex = StringHelper.GetIndexOfStr(rawValue, "\"", );
yhText = rawValue.Substring(yhBeginIndex, (yhEndIndex - yhBeginIndex + ));
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)
{
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(, path.LastIndexOf('\\'));
}
else
{
fileDirectoryPath = path.Substring(, 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 = ;
int tIndex = ; using (StreamWriter streamWriter = new StreamWriter(path, false, fileDescription.Encoding))
{
do
{
try
{
rawValue = ""; #if DEBUG
if (currentRawIndex % == )
{
this._Logger.LogInformation($"已写入文件:{path},数据量:{currentRawIndex}");
}
#endif if (currentRawIndex >= fileDescription.TitleRawIndex)
{
//清空数组数据
for (int i = ; 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)
{
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()
{
}
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>(); 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 = -; int currentPosition = ;
if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(str) && strPosition >= )
{
do
{
currentPosition++;
if (strIndex == -)
{
strIndex = text.IndexOf(str);
}
else
{
strIndex = text.IndexOf(str, strIndex + );
}
} while (currentPosition < strPosition);
} return strIndex;
}
}
}

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

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

  1. C#使用Linq to csv读取.csv文件数据

    前言:今日遇到了一个需要读取CSV文件类型的EXCEL文档数据的问题,原本使用NPOI的解决方案直接读取文档数据,最后失败了,主要是文件的类型版本等信息不兼容导致.其他同事有使用linq to csv ...

  2. matlab读取csv文件数据并绘图

    circle.m(画二维圆的函数) %该函数是画二维圆圈,输入圆心坐标和半径%rectangle()函数参数‘linewidth’修饰曲线的宽度%'edgecolor','r',edgecolor表示 ...

  3. python读取csv文件数据绘制图像,例子绘制天气每天最高最低气温气象图

  4. C#使用Linq to csv读取.csv文件数据2_处理含有非列名数据的方法(说明信息等)

    第一篇博客为:https://www.cnblogs.com/lxhbky/p/11884474.html 本文主要是为了解决上面博客遗留的一个含有不规范数据的一种方法,目前暂时没有从包里发现可以从第 ...

  5. VB 读取csv文件数据

    Public adoConn As New ADODB.Connection Private Sub csv() adoConn.ConnectionString = "Driver={Mi ...

  6. php 生成读取csv文件并解决中文乱码

    csv其实是文本文件,但是里面的内容是利用逗号分隔的. 1. 生成csv文件 function new_csv($arr) { $string=""; foreach ($arr ...

  7. NET Core 2.0 自定义

    ASP.NET Core 2.0 自定义 _ViewStart 和 _ViewImports 的目录位置 在 ASP.NET Core 里扩展 Razor 查找视图目录不是什么新鲜和困难的事情,但 _ ...

  8. ASP.NET Core 5.0 中读取Request中Body信息

    ASP.NET Core 5.0 中读取Request中Body信息 记录一下如何读取Request中Body信息 public class ValuesController : Controller ...

  9. VB6.0 读取CSV文件

    最近做了一个Upload文件的需求,文件的格式为CSV,读取文件的方法整理了一下,如下: 1.先写了一个读取CSV文件的Function: '读取CSV文件 '假设传入的参数strFile=C:\Do ...

随机推荐

  1. Eclipse CDT 插件修改自动补全

    eclipse CDT 2019-06代码补全插件 本自动补全文件已在2019-06至2019-09平台上做过测试,均已完美通过功能检测 在原来Eclipse工具补全的基础上新增26个英文字符和&qu ...

  2. 每日一技|巧用 Telnet 调试 Dubbo 服务

    个人博客地址 studyidea.cn,点击查看更多原创文章 0x00. 前言 想象这样一个场景,线上某个服务突发异常,导致上游服务调用异常,数据处于中间状态.服务恢复之后,我们需要修复这笔数据至正常 ...

  3. Shell常用命令之主机检测(ping)

    主机检测命令ping 向网络主机发送ICMP回传请求 常用选项 -A:洪水攻击选项,启用此功能能在短时间之内发送大量的ping包 -b:开启ping网桥模式,默认不允许ping网桥 -c:设置发送多少 ...

  4. Leetcode 题目整理 climbing stairs

    You are climbing a stair case. It takes n steps to reach to the top. Each time you can either climb ...

  5. JVM性能分析 | 一次生产系统Full GC问题分析与排查总结

    一次生产系统Full GC问题分析与排查总结 背景 最近某线上业务系统生产环境频频CPU使用率过低,频繁告警,通过重启可以缓解,但是过了一段时间又会继续预警,线上两个服务节点相继出现CPU资源紧张,导 ...

  6. CUDA学习(七)之使用CUDA内置API计时

    问题:对于使用GPU计算时,都想知道kernel函数运行所耗费的时间,使用CUDA内置的API可以方便准确的获得kernel运行时间. 在CPU上,可以使用clock()函数和GetTickCount ...

  7. Windows玩转Kubernetes系列3-Centos安装K8S

    以往文章参考: Windows玩转Kubernetes系列1-VirtualBox安装Centos Windows玩转Kubernetes系列2-Centos安装Docker 安装K8S yum in ...

  8. SpringBoot之切面AOP

    SpringBoot提供了强大AOP支持,我们前面讲解过AOP面向切面,所以这里具体AOP原理就补具体介绍: AOP切面主要是切方法,我们一般搞一些日志分析和事务操作,要用到切面,类似拦截器: @As ...

  9. Excel VBA: 自动生成巡检报表并通过邮件定时发送

    目录 环境说明逻辑结构效果说明及截图①.安装SecureCRT②. 自动巡检脚本③. 数据检索并FTP传送④. 安装Excel 2013⑤. 安装Serv-U⑥. 自动生成图表并邮件发送 环境说明 系 ...

  10. MySQL关系型数据库基础操作

    MySQL基础 一.MySQL常用数据类型 1.常用数值类型(INT,DOUBLE,FLOAT) ① int 或者 integer 类型: 大小(字节):4字节: 范围: (有符号: -2147483 ...