CSV 文件的读写

Demo

var originFile = @"D:\Demo.csv";
var list = CsvHelper.Read<DemoCsvEntity>(originFile);
var filePath = @"D:\Rslt.csv";
CsvHelper.Write(list, filePath, true);

其中,DemoCsvEntity的属性的顺序是固定的,与csv文件内的表头内容保持一致。

using Microsoft.VisualBasic.FileIO;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;

public class CsvHelper
{
    /// <summary>
    /// 读CSV文件
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="path">文件路径</param>
    /// <param name="func">数值转换方法</param>
    /// <param name="encoding">编码格式</param>
    /// <returns></returns>
    public static List<T> Read<T>(string path, Func<string[], T> func = null, Encoding encoding = null) where T : class
    {
        if (encoding == null)
        {
            encoding = Encoding.UTF8;
        }
        if (func == null)
        {
            func = SetObjValues<T>;
        }
        var rslt = new List<T>();
        using (var parser = new TextFieldParser(path, encoding))
        {
            parser.TextFieldType = FieldType.Delimited;
            parser.SetDelimiters(",");
            parser.TrimWhiteSpace = false;  // 不忽略字段前后的空格
            bool isLine = false;
            while (!parser.EndOfData)
            {
                string[] fields = parser.ReadFields();
                if (isLine)
                {
                    var obj = func(fields);
                    if (obj != null)
                    {
                        rslt.Add(obj);
                    }
                }
                else
                {
                    // 忽略标题行
                    isLine = true;
                }
            }
        }
        return rslt;
    }
    /// <summary>
    /// 写CSV文件
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="data">数据集合</param>
    /// <param name="path">文件路径</param>
    /// <param name="append">true追加,false不追加新建</param>
    /// <param name="func">字段转换方法</param>
    /// <param name="encoding">编码格式</param>
    public static void Write<T>(IList<T> data, string path, bool append = false, Func<T, bool, IEnumerable<string>> func = null, Encoding encoding = null) where T : class
    {
        if (data == null || data.Count == 0)
        {
            return;
        }
        if (encoding == null)
        {
            encoding = Encoding.UTF8;
        }
        if (func == null)
        {
            func = GetObjValues;
        }
        try
        {
            if (!File.Exists(path) || !append)
            {
                var fields = func(data[0], true);
                string title = FieldsToLine(fields);
                File.WriteAllText(path, title, encoding);
            }
            using (var sw = new StreamWriter(path, true, encoding))
            {
                foreach (var item in data)
                {
                    var fields = func(item, false);
                    string line = FieldsToLine(fields);
                    sw.Write(line);
                }
            }
        }
        catch (Exception ex)
        {
            throw new InvalidOperationException("写入失败!" + ex.Message);
        }
    }

    /// <summary>
    /// 字段拼接一行
    /// </summary>
    /// <param name="fields"></param>
    /// <returns></returns>
    private static string FieldsToLine(IEnumerable<string> fields)
    {
        if (fields == null)
        {
            return string.Empty;
        }
        var rslt = fields.Select(x =>
        {
            if (string.IsNullOrWhiteSpace(x))
            {
                return string.Empty;
            }
            // 所有字段都加双引号,按需使用
            //x = string.Format("\"{0}\"", x.Replace("\"", "\"\""));
            return x;
        });
        return string.Format("{0}{1}", string.Join(",", rslt), Environment.NewLine);
    }

    /// <summary>
    /// 设置对象值
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="data">值集合</param>
    /// <returns></returns>
    private static T SetObjValues<T>(string[] data) where T : class
    {
        T rslt = Activator.CreateInstance<T>();
        var type = typeof(T);
        try
        {
            var properties = type.GetProperties();
            // 确保值的顺序与属性顺序一致,且值类型也一致
            for (int i = 0; i < data.Length; i++)
            {
                var pi = properties[i];
                var typeConverter = TypeDescriptor.GetConverter(pi.PropertyType);
                var value = typeConverter.ConvertFromString(data[i]);
                type.InvokeMember(pi.Name, System.Reflection.BindingFlags.SetProperty, Type.DefaultBinder, rslt, new object[] { value });
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        return rslt;
    }

    /// <summary>
    /// 获取对象形式值
    /// </summary>
    /// <typeparam name="T">实体类型</typeparam>
    /// <param name="obj">实体对象</param>
    /// <param name="isTitle">true,行首标题;false,每行值</param>
    /// <returns></returns>
    private static IEnumerable<string> GetObjValues<T>(T obj, bool isTitle) where T : class
    {
        IEnumerable<string> rslt = null;
        if (isTitle)
        {
            rslt = obj.GetType().GetProperties().Select(x => x.Name);
        }
        else
        {
            rslt = obj.GetType().GetProperties().Select(x =>
            {
                if (x.GetValue(obj) == null)
                {
                    return string.Empty;
                }
                return x.GetValue(obj).ToString();
            });
        }
        return rslt;
    }
}

自定义

CsvHelper 的读写方法中提供默认的转换逻辑,如果想兼容复杂情形,可以自定义转换。

SetObjValues

Read 方法中,SetObjVlues 根据值的字符串集合,设置对象的不同属性值。为保证正确转换,需要确保集合中值的属性与对象的属性顺序是一致的,避免出现转换异常的情况。

如果需要根据文本值,特别设置对应属性的值,可以在 SetObjVlues 的基础上进行扩展。

private T SetObjValues<T>(string[] data) where T: class
{
    T rslt = Activator.CreateInstance<T>();
    var type = typeof(T);
    try
    {
        var properties = type.GetProperties();
        for (int i = 0; i < data.Length; i++)
        {
            var pi = properties[i];
            if (pi.Name == "bPropertName" || pi.Name == "bPropertName2" || pi.Name == "bPropertName3" )
            {
                if (data[i] == "1")  // 整数转换为bool
                {
                    pi.SetValue(rslt, true);
                }
                else
                {
                    pi.SetValue(rslt, false);
                }
            }
            else if (pi.Name == "PropertyName3")   // 指定属性的值
            {
                if (data[i] == "0")
                {
                    pi.SetValue(rslt, "OK");
                }
                else
                {
                    pi.SetValue(rslt, "ERROR");
                }
            }
            else    // Default
            {
                var typeConverter = TypeDescriptor.GetConverter(pi.PropertyType);
                var value = typeConverter.ConvertFromString(data[i]);
                type.InvokeMember(pi.Name, System.Reflection.BindingFlags.SetProperty, Type.DefaultBinder, rslt, new object[] { value });
            }
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
    return rslt;
}

GetObjValues

如果需要根据属性值,特别显示对应的文本,可以在 GetObjValues 的基础上进行扩展。

private static IEnumerable<string> GetObjValues<T>(T obj, bool isTitle) where T : class
{
    IEnumerable<string> rslt = null;
    if (isTitle)
    {
        rslt = obj.GetType().GetProperties().Select(x => x.Name);
    }
    else
    {
        rslt = obj.GetType().GetProperties().Select(x =>
        {
            // bool值用整数显示
            if (x.PropertyType == typeof(bool))
            {
                return (bool)x.GetValue(obj) == true ? "1" : "0";
            }
            // 指定属性的值用整数显示
            else if (x.Name == "PropertyName3")
            {
                return (string)x.GetValue(obj) == "OK" ? "0" : "1";
            }
            // default
            return x.GetValue(obj) == null ? string.Empty : x.GetValue(obj).ToString();
        });
    }
    return rslt;
}
posted @ 2023-04-27 15:30  wesson2019  阅读(41)  评论(0编辑  收藏  举报