[筆記] 解決 CsvHelper 套件讀取科學記號數字問題

  • 238
  • 0

CsvHelper 套件是我蠻喜歡用來讀寫 CSV 檔案的一個套件,可以很簡單的上手,又可以讀取資料時候直接轉成強型別,最近再處理一堆數字類型的資料因為有些資料使用科學記號的方式呈現,預設會解析失敗,後來找到一些解法就稍做記錄一下,避免之後又遇到。

實做

使用的範例資料如下,其中 Quanity 欄位的數字是科學記號呈現的資料。

專案新增 CsvHelper 套件

主程式就讀取一個 Csv 檔案,透過泛型方法讀取資料之後轉成強型別資料。

internal class Program
{
    static void Main(string[] args)
    {
        var path = @"{Your Csv File Path}";
        var datas = LoadCSV<SampleModel>(path);

        foreach (var data in datas)
        {
            Console.WriteLine($"{data.Id}\t{data.ServiceName}\t{data.Quantity}");
        }
    }

    public static List<T> LoadCSV<T>(string path)
    {
        var csvString = File.ReadAllText(path);

        var config = new CsvConfiguration(CultureInfo.InvariantCulture);
        using (var reader = new StringReader(csvString))
        using (var csv = new CsvReader(reader, config))
        {
            return csv.GetRecords<T>().ToList();
        }
    }
}

定義資料轉換的強型別的類別。

public class SampleModel
{
    public int Id { get; set; }
    public string ServiceName { get; set; }
    public decimal Quantity { get; set; }
}

因為預設不支援科學記號的數字解析,所以出現了錯誤訊息。

解法1

直接在 Grobal 設定所有 decimal 資料都可以使用科學記號呈現。

var config = new CsvConfiguration(CultureInfo.InvariantCulture);

using (var reader = new StringReader(csvString))
using (var csv = new CsvReader(reader, config))
{
    csv.Context.TypeConverterOptionsCache.GetOptions<decimal>().NumberStyles = NumberStyles.Number | NumberStyles.AllowExponent; // Add This
    return csv.GetRecords<T>().ToList();
}

解法2

針對特定的類別欄位增加 Attribute 標記允許科學記號資料。

public class SampleModel
{
    public int Id { get; set; }
    public string ServiceName { get; set; }
    [NumberStyles(NumberStyles.Number | NumberStyles.AllowExponent)]
    public decimal Quantity { get; set; }
}

解法3

新增一個 Mapping 類別並且註冊使用。

public class SampleMap : ClassMap<SampleModel>
{
    public SampleMap()
    {
        Map(m => m.Id);
        Map(m => m.ServiceName);
        Map(m => m.Quantity).TypeConverterOption.NumberStyles(NumberStyles.Number | NumberStyles.AllowExponent);
    }
}
var config = new CsvConfiguration(CultureInfo.InvariantCulture);

using (var reader = new StringReader(csvString))
using (var csv = new CsvReader(reader, config))
{
    csv.Context.RegisterClassMap<SampleMap>(); // Register ClassMap
    return csv.GetRecords<T>().ToList();
}

透過以上解法就都可以正確的讀到資料了。

參考資料