FileHelper 如何使用,以及效能問題

套件的使用有使用上的方便性,但如果遇到效能問題,究竟誰勝誰負

FileHelper簡單介紹

官網上可以發現,有讀、寫功能,固定的長度或是特定符號分割例如CSV檔

例如電文是固定長度格式,那麼就可以採用官網上的Read Fixed File

Class需要如下列這樣編寫

[FixedLengthRecord()] //宣告該物件使用固定長度
public class FileModle
{
	[FieldFixedLength(10)] //抓取長度10
	[FieldTrim(TrimMode.Both)] //讀取後執行Trim
	public string? Name { get; set; }
	[FieldFixedLength(10)]
	FieldTrim(TrimMode.Both)]
	public string? Phone { get; set; }
	[FieldFixedLength(100)]
	[FieldTrim(TrimMode.Both)]
	public string? Description { get; set; }
	[FieldFixedLength(100)]
	[FieldTrim(TrimMode.Both)]
	[FieldOptional()] //如果該欄位不一定會有,則可以使用FieldOptional
	[FieldNullValue(typeof(string), "")] //如果該欄位不一定會有,但也要給他初始值則可以使用FieldNullValue
	public string? Else { get; set; }
}

用法可以如此編寫

var engine = new FixedFileEngine<FileModle>();
var result1 = engine.ReadFile(path);

在使用上確實非常簡潔可讀性也高,但如果有遇到大資料量時效能又是如何

所以本次利用常用的方式(SubString)來做個對比

環境:VS2022、Net6 Console

完整程式碼為

using FileHelpers;
using System.Text;
using TestFileHelper;

Console.WriteLine("Hello");

string path = @"e:\temp\MyTest.txt";

if (File.Exists(path))
{
    File.Delete(path);
    FileStream fs = File.Create(path);
    string item = "1234567890098765432100000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999998888888888999999999966666666667777777777444444444455555555552222222222333333333300000000001111111111";
    int count = 1000000;
    for (int i = 0; i < 1000000; i++)
    {
        string data = item;

        if (i != count)
            data += "\r\n";

        byte[] info = new UTF8Encoding(true).GetBytes(data);
        fs.Write(info, 0, info.Length);
    }
    fs.Close();
}

Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff") + " FileHelper Start");
var engine = new FixedFileEngine<FileModle>();
var result1 = engine.ReadFile(path);
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff") + " FileHelper End");
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff") + " SubString Start");
SubStringSplit subStringSplit = new();
var result2 = subStringSplit.Execute(path);
Console.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff") + " SubString End");
Console.ReadLine();

使用到兩個物件

using FileHelpers;

namespace TestFileHelper
{
    [FixedLengthRecord()]
    public class FileModle
    {
        [FieldFixedLength(10)]
        [FieldTrim(TrimMode.Both)]
        public string? Name { get; set; }
        [FieldFixedLength(10)]
        [FieldTrim(TrimMode.Both)]
        public string? Phone { get; set; }
        [FieldFixedLength(100)]
        [FieldTrim(TrimMode.Both)]
        public string? Description { get; set; }
        [FieldFixedLength(100)]
        [FieldTrim(TrimMode.Both)]
        [FieldOptional()]
        [FieldNullValue(typeof(string), "")]
        public string? Else { get; set; }
    }

    public class SubStringSplit
    {
        public IList<FileModle> Execute(string path)
        {
            List<FileModle> result = new();
            using StreamReader sr = new(path);
            string? line = null;
            while ((line = sr.ReadLine()) != null)
            {
                if(line.Length >= 120)
                {
                    FileModle fileModle = new()
                    {
                        Name = line.Substring(0, 10).Trim(),
                        Phone = line.Substring(10, 10).Trim(),
                        Description = line.Substring(20, 100).Trim()
                    };
                    if (line.Length == 220)
                        fileModle.Else = line.Substring(120, 100).Trim();
                    else
                        fileModle.Else = "";
                    result.Add(fileModle);
                }
            }
            return result;
        }
    }
}

本次使用一百萬筆的數據量,可以看到大約都在三秒上下,相差0.2~0.5秒左右

但如果觀看記憶體的使用量呢

FileHelper花費3.322秒,記憶體使用931MB

SubString花費2.941秒,記憶體使用761MB

最後總結SubString在一百萬筆的資料量下,效能略勝,且記憶體使用的少

資料來源參考

  1. filehelpers