Fork me on GitHub

撸码是需要直觉的

随着撸码的时间增加,码感也会加强,今天看一个编码直觉案例吧!

 

案例:

需求:把查询到的数据列表生成csv文件

nuget包:CsvHelper

 

数据实体类:

    /// <summary>
    /// 商品
    /// </summary>
    public class Goods
    {
        /// <summary>
        /// 编号
        /// </summary>
        public uint ID
        { get; set; }
        /// <summary>
        /// 名称
        /// </summary>
        public string Name
        { get; set; }
        /// <summary>
        /// 规格
        /// </summary>
        public string Spec
        { get; set; }
        /// <summary>
        /// 单位
        /// </summary>
        public string Unit
        { get; set; }
        /// <summary>
        /// 制造商
        /// </summary>
        public string Manufacturer
        { get; set; }
        /// <summary>
        /// 价格
        /// </summary>
        public decimal Price
        { get; set; }
        /// <summary>
        /// 这里是为了打印数据方例重写ToString()
        /// </summary>
        public override string ToString()
        {
            return $"ID={ID},Name={Name},Unit={Unit},Spec={Spec},Price={Price},Manufacturer={Manufacturer}";
        }
    }

定义了一个csv操作的接口:

public interface ICsvHandle
{
    List<Goods> ReadCsv(string file);
    void WriteCsv(string file, List<Goods> goodses);
}

  为了演示方便,Demo中的数据列表是通过Bogus产生(bogus在之前的文章中介绍过)

  static List<Goods> GoodsCreater()
        {
            var goodses = new List<Goods>();
            var options = new JsonSerializerOptions();
            options.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.Create(UnicodeRanges.All);
            for (var i = 0; i < 100; i++)
            {
                var goodsFaker = new Faker<Goods>("zh_CN")
                    .RuleFor(x => x.ID, x => x.Random.UInt())
                    .RuleFor(x => x.Name, x => x.Random.ArrayElement(
                        new string[]
                        {
 "甲销唑注射液",
"葡萄糖注射液",
"盐水(氯化钠)",
"利巴韦林病毒唑针",
"氟康唑氯化钠注射液(康锐)",
"安痛定针",
"维生素B12针",
"硫酸阿米卡星注射液",
"氯化钠注射液(塑料瓶)",
"转移因子注射液",
"血塞通粉针",
"苦碟子注射液",
"注射用培美曲塞二钠(爱立汀)",
"左克注射液",
"维生素C注射液",
"硫酸庆大霉素注射液",
"盐酸左氧氟沙星注射液",
"蔗糖铁注射液",
"注射用青霉素钠",
"维生素B1针",
"疏血通注射液",
"胃复安盐酸甲氧氯普胺注射液",
"硫酸庆大霉素注射液",
"葡萄糖注射液50%",
"银杏达莫注射液",
"硫酸庆大霉素注射液",
"5%葡萄糖注射液",
"注射用氨曲南",
                        }
                        ))
                    .RuleFor(x => x.Price, x => x.Random.Decimal())
                    .RuleFor(x => x.Unit, x => x.Random.ArrayElement(new string[] { "", "", "", "", "", "" }))
                    .RuleFor(x => x.Spec, x => x.Random.ArrayElement(new string[]
                       {
 "5*20ml",
"250ml",
"1ml*10支",
"100ml:0.2g",
"2毫升*10支",
"10支",
"2ml*10.2克*10支",
"100ml",
"3毫升:2毫升*10支",
"20毫克",
"10mg*10支",
"0.5克",
"2ml:0.1g",
"0.25G*2ML",
"4万单位*10支",
"100ml*0.1g",
"1g",
"80万单位",
"2ml*10支",
"5毫克*60片",
"1ml:10mg",
"4万*2ml*10支",
"20ML*5支",
"5ml",
"2ml:40mg(4万单位)",
"250ml",
"0.5克*10支",
"5ML;17.5MG",
"2ML:0.5G*10支",
                       }))
                    .RuleFor(x => x.Manufacturer, x => x.Random.ArrayElement(
                        new string[]
                    {
 "山东齐都药业/山西云鹏",
"侯马霸王药业/山西晋新双鹤/天津新郑/西安/贵州",
"山西临汾云鹏药业、山东齐都药业/山西银湖/石家庄四药/河南科伦",
"安徽联谊/无锡",
"扬子江药业集团",
"新乡常乐制药/山西太原药业/天津焦作/郑州",
"天津药业焦作",
"山东方明药业集团股份有限公司",
"石家庄四药有限公司",
"湖南一格制药有限公司",
"哈尔滨",
"通化华夏制药有限责任公司",
"德州德药制药有限公司",
"扬子江药业",
"山西晋新双鹤药业/新乡市新辉药业/河南润弘",
"河南辅仁怀庆堂制药/濮阳市汇元药业/新乡常乐",
"广西裕源药业、四川科伦大药厂",
"成都天台山制药有限公司",
"华北制药",
"安阳九州药业、山东方明药业/石药银湖制药",
"牡丹江友博药业有限公司",
"濮阳汇元/天津药业集团新郑",
"天津药业焦作/侯马",
"湖南科伦/焦作民康",
"贵州益佰制药股份有限公司",
"新乡市新辉药业/新乡常乐制药/濮阳市汇元药业",
"山东华鲁制药/山东齐都/山西云鹏制药/西安汉丰",
"重庆圣华曦药业",
                    }));
                goodses.Add(goodsFaker.Generate());
            }
            return goodses;
        }

第一版:

csv文件格式是utf8

public class BasisCsvHandle : ICsvHandle
{
    public List<Goods> ReadCsv(string file)
    {
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            NewLine = "\r\n"
        };
        using (var reader = new StreamReader(file))
        using (var csv = new CsvReader(reader, config))
        {
            var goodses = csv.GetRecords<Goods>();
            return goodses.ToList();
        }
    }
    public void WriteCsv(string file, List<Goods> goodses)
    {
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            NewLine = "\r\n"
        };
        using (var writer = new StreamWriter(file))
        {
            using (var csv = new CsvWriter(writer, config))
            {
                csv.WriteRecords(goodses);
            }
        }
    }
}

  后来有的客户要BOM类型的UTF8,其实就是在文件开头加上byte[] BOM = { 0xEF, 0xBB, 0xBF };这个字节数组就ok

  为了适配合有无BOM和非BOM的UTF8,对接口进行了重构,增加默认属性(正好练一下接口默认属性的用法),这种做法的好处是不打扰BasisCsvHandle的代码,当然,也可以更深层的重构ICsvHandle中的两个方法来适配是否是BOM UTF8

public interface ICsvHandle
{
    List<Goods> ReadCsv(string file);
    void WriteCsv(string file, List<Goods> goodses);
    private static bool _isBOM;
    public bool IsBOM { get => _isBOM; set => _isBOM = value; }
}

第二版:

public class StrengthenCsvHandle : ICsvHandle
{
    public List<Goods> ReadCsv(string file)
    {
        MemoryStream memory = null;
        using (var reader = new FileStream(file, FileMode.OpenOrCreate, FileAccess.ReadWrite))
        {
            var bytes = new byte[reader.Length];
            reader.Read(bytes, 0, bytes.Length);
            if (((ICsvHandle)this).IsBOM)
            {
                bytes = bytes.Skip(3).Take(bytes.Length - 3).ToArray();
            }
            memory = new MemoryStream(bytes);
        }
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            NewLine = "\r\n"
        };
        using (var reader = new StreamReader(memory))
        using (var csv = new CsvReader(reader, config))
        {
            var goodses = csv.GetRecords<Goods>();
            return goodses.ToList();
        }
    }
    public void WriteCsv(string file, List<Goods> goodses)
    {
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            NewLine = "\r\n"
        };
        using (var writer = new StreamWriter(file))
        {
            using (var csv = new CsvWriter(writer, config))
            {
                csv.WriteRecords(goodses);
            }
        }
        if (((ICsvHandle)this).IsBOM)
        {
            //追究加BOM
            using (var writer = new FileStream(file, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                var bytes = new byte[writer.Length];
                writer.Read(bytes, 0, bytes.Length);
                byte[] BOM = { 0xEF, 0xBB, 0xBF };
                var list = new List<byte>();
                list.AddRange(BOM);
                list.AddRange(bytes);
                writer.Position = 0;
                writer.Write(list.ToArray(), 0, list.Count);
            }
        }
    }
}

  虽然能实现功能,但是觉得这样的代码太啰嗦,这里的本质就是在写数据时加上三个字节,读取数据时,少读三个字节直觉告诉我,应该好像也许或者能有更简单的方式。

第三版:

public class PerfectCsvHandle : ICsvHandle
{
    public List<Goods> ReadCsv(string file)
    {
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            NewLine = "\r\n"
        };
        using (var reader = new StreamReader(file))
        using (var csv = new CsvReader(reader, config))
        {
            if (((ICsvHandle)this).IsBOM)
            {
                reader.BaseStream.Position = 3;
            }
            var goodses = csv.GetRecords<Goods>();
            return goodses.ToList();
        }

    }
    public void WriteCsv(string file, List<Goods> goodses)
    {
        var config = new CsvConfiguration(CultureInfo.InvariantCulture)
        {
            NewLine = "\r\n"
        };

        using (var writer = new StreamWriter(file))
        {
            using (var csv = new CsvWriter(writer, config))
            {
                if (((ICsvHandle)this).IsBOM)
                {
                    byte[] BOM = { 0xEF, 0xBB, 0xBF };
                    writer.Write(System.Text.Encoding.UTF8.GetString(BOM));
                }
                csv.WriteRecords(goodses);
            }
        }
    }
}

 

  想要更快更方便的了解相关知识,可以关注微信公众号 
 

 

 

posted @ 2022-02-04 17:01  桂素伟  阅读(98)  评论(0编辑  收藏  举报