使用 csvHelper 处理 csv 文件中的无效条目

Handle invlaid entries in csv file with csv Helper

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

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

据我了解,这是因为csv中的field population是空的。我目前希望能够验证该字段并将其设置为 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;


        }

    }
}

我的映射 class 是


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

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

        }


    }

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

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

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

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

我还添加了一个 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;
    }
}

此致。

您可以使用 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; }
}