【问题标题】:Handle invlaid entries in csv file with csv Helper使用 csvHelper 处理 csv 文件中的无效条目
【发布时间】:2020-03-19 17:22:41
【问题描述】:

我目前正在使用 CSV 帮助程序来读取 csv 文件的内容并将其输出到控制台。我已经安装了 csvHelper nuget 包。但是,当我运行代码时,出现以下错误:

CsvHelper.TypeConversion.TypeConverterException: '无法执行转换。 文本: '' 会员类型: TypeConverter: 'CsvHelper.TypeConversion.Int32Converter''

我知道这是因为 csv 中的字段填充为空。我目前希望能够验证该字段并将其设置为 0。如何使用 CSVhelper 执行此操作。

我读取 csv 的代码是:

class ReaderCsv
    {
        private string _cvsfilepath;

        public ReaderCsv(string csvfilepath)
        {
            this._cvsfilepath = csvfilepath;
        }

        public List <Country>  ReadAllCountries()
        {
            var countries = new List<Country>();
            using (var sr = new StreamReader(_cvsfilepath))
            using (var csv = new CsvReader(sr, System.Globalization.CultureInfo.InvariantCulture))
            {


                csv.Configuration.Delimiter = ",";
                csv.Read();
                csv.ReadHeader();

                while (csv.Read()) 
                {
                    var country= new Country();

                    { 
                        country.CountryName = csv.GetField("CountryName");
                        country.CountryCode = csv.GetField("CountryCode");
                        country.Continent = csv.GetField("CountryCode");
                        country.Population = csv.GetField<int>("Population");


                    }
                    countries.Add(country);

                }
            }return countries;


        }

    }
}

我的映射类是


public class CountryMap  : ClassMap<Country>
    {
        public CountryMap()
        {

            Map(m => m.CountryName);
            Map(m => m.CountryCode);
            Map(m => m.Continent);
            Map(m => m.Population);

        }


    }

【问题讨论】:

  • stackoverflow.com/questions/736629/parse-delimited-csv-in-net 它在 Visual Basic 命名空间中,但仍然有效。坏数据就是坏数据,你必须决定要么拒绝它,要么尝试解决它。 Rejection is protection 我的一个朋友经常这么说。
  • 嗨。如果我的建议解决了您的问题,您可以标记为答案吗?

标签: c# .net csv csvhelper


【解决方案1】:

CSV Helper 提供 GetField 方法的重载,您可以将自定义类型转换器传递给该方法。

https://joshclose.github.io/CsvHelper/api/CsvHelper/CsvReader/

因此;不仅适用于 Int32,而且适用于任何类型,这里有一个使用自定义泛型类型转换器的实现,如果转换失败,它会返回类型的默认值。

这并不意味着您必须吞下或忽略异常。此转换器还会为您提供转换错误和违规值,以便您可以处理这些无效数据。

我还添加了一个 lineNumber 变量来跟踪无效数据所在的行。

我希望这会有所帮助。

public class Defaulter<T> : CsvHelper.TypeConversion.ITypeConverter
{
    Exception conversionError;
    string offendingValue;

    public Exception GetLastError()
    {
        return conversionError;
    }

    public string GetOffendingValue()
    {
        return offendingValue;
    }

    object CsvHelper.TypeConversion.ITypeConverter.ConvertFromString(string text, IReaderRow row, CsvHelper.Configuration.MemberMapData memberMapData)
    {
        conversionError = null;
        offendingValue = null;
        try
        {
            return (T)Convert.ChangeType(text, typeof(T));
        }
        catch (Exception localConversionError)
        {
            conversionError = localConversionError;
        }
        return default(T);
    }

    string CsvHelper.TypeConversion.ITypeConverter.ConvertToString(object value, IWriterRow row, CsvHelper.Configuration.MemberMapData memberMapData)
    {
        return Convert.ToString(value);
    }
}

这是您的代码的修改版本,用于跟踪行号以及根据需要处理错误:

public class ReaderCsv
{
    private string _cvsfilepath;

    public ReaderCsv(string csvfilepath)
    {
        this._cvsfilepath = csvfilepath;
    }

    public List<Country> ReadAllCountries()
    {
        var countries = new List<Country>();
        using (var sr = new StreamReader(_cvsfilepath))
        using (var csv = new CsvReader(sr, System.Globalization.CultureInfo.InvariantCulture))
        {
            csv.Configuration.Delimiter = ",";
            csv.Read();
            csv.ReadHeader();

            Defaulter<int> customInt32Converter = new Defaulter<int>();

            int lineNumber = 0;

            while (csv.Read())
            {
                lineNumber++;
                var country = new Country();
                {
                    country.CountryName = csv.GetField("CountryName");
                    country.CountryCode = csv.GetField("CountryCode");
                    country.Continent = csv.GetField("CountryCode");
                    country.Population = csv.GetField<int>("Population", customInt32Converter);

                    if (customInt32Converter.GetLastError() != null)
                    {
                        // The last conversion has failed.
                        // Handle it here.
                        string errorMessage = "The conversion of Population field on line " + lineNumber + " has failed. The Population value was: [" + customInt32Converter.GetOffendingValue() + "]";

                    }
                }
                countries.Add(country);

            }
        }
        return countries;
    }
}

问候。

【讨论】:

    【解决方案2】:

    您可以使用ClassMapPopulation 提供默认值

    public class Program
    {
        public static void Main(string[] args)
        {
            using (MemoryStream stream = new MemoryStream())
            using (StreamWriter writer = new StreamWriter(stream))
            using (StreamReader reader = new StreamReader(stream))
            using (CsvReader csv = new CsvReader(reader, CultureInfo.InvariantCulture))
            {
                writer.WriteLine("CountryName,CountryCode,Continent,Population");
                writer.WriteLine("TestName1,TestCode1,TestContinent1,");
                writer.WriteLine("TestName2,TestCode2,TestContinent2,2");
                writer.Flush();
                stream.Position = 0;
    
                csv.Configuration.RegisterClassMap<CountryMap>();
    
                var countries = csv.GetRecords<Country>().ToList();
            }
    
            Console.ReadKey();
        }
    }
    
    public class CountryMap : ClassMap<Country>
    {
        public CountryMap()
        {
            Map(m => m.CountryName);
            Map(m => m.CountryCode);
            Map(m => m.Continent);
            Map(m => m.Population).Default(0);
        }
    }
    
    public class Country
    {
        public string CountryName { get; set; }
        public string CountryCode { get; set; }
        public string Continent { get; set; }
        public int Population { get; set; }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-04
      相关资源
      最近更新 更多