如何在FileHelpers中使用动态CSV分隔符

本文关键字:动态 CSV 分隔符 FileHelpers | 更新日期: 2023-09-27 18:19:35

问题:我需要阅读CSV文件。我使用FileHelpers库来实现这一点。

问题是我需要一个动态分隔符(用户定义),这意味着任何东西都可以是分隔符(逗号、分号、制表符、换行符,还有其他任何东西)。

问题是,FileHelpers在属性中定义了分隔符,这意味着在编译时。这使得它不可能动态地执行。

我可以做的是声明一个新类,它继承自一个基类,并在这个新类上设置分隔符。

[FileHelpers.DelimitedRecord(",")]
public class CommaCustomer : BaseCustomer
{
}

这样,我只需要为每个新的分隔符在基类中进行更改。问题是,我不能(也不想)为每个可能的分隔符创建一个子类。

这是我迄今为止的代码:

using System;
using System.Data;
using System.IO;
//using FileHelpers;
//using FileHelpers.RunTime;

namespace Examples
{

    class MainClass
    {

        [STAThread]
        static void Main()
        {
            FileHelpers.FileHelperEngine engine = new FileHelpers.FileHelperEngine(typeof(SemicolonCustomer));
            // To read use:
            string str = @"D:'Username'Desktop'FileHelpers_Examples_CSharp_VbNet'Data'SemicolonCustomers.txt";
            //str = @"D:'Username'Desktop'FileHelpers_Examples_CSharp_VbNet'Data'CustomersDelimited.txt";
            SemicolonCustomer[] custs = (SemicolonCustomer[])engine.ReadFile(str);
            //Customer[] custs = (Customer[]) engine.ReadFile("yourfile.txt");

            foreach (SemicolonCustomer cli in custs)
            {
                Console.WriteLine();
                Console.WriteLine("Customer: " + cli.CustId.ToString() + " - " + cli.Name);
                Console.WriteLine("Added Date: " + cli.AddedDate.ToString("d-M-yyyy"));
                Console.WriteLine("Balance: " + cli.Balance.ToString());
                Console.WriteLine();
                Console.WriteLine("-----------------------------");
            } // Next cli
            Console.ReadKey();
            Console.WriteLine("Writing data to a delimited file...");
            Console.WriteLine();

            // To write use:
            //engine.WriteFile("myyourfile.txt", custs);

            //If you are using .NET 2.0 or greater is 
            //better if you use the Generics version:
            // FileHelperEngine engine = new FileHelperEngine<Customer>();
            // To read use (no casts =)
            // Customer[] custs = engine.ReadFile("yourfile.txt");
            // To write use:
            // engine.WriteFile("yourfile.txt", custs);
        } // End Sub Main

    } // End Class  MainClass

    //------------------------
    //   RECORD CLASS (Example, change at your will)
    //   TIP: Remember to use the wizard to generate this class
    public class BaseCustomer
    {
        public int CustId;
        public string Name;
        public decimal Balance;
        [FileHelpers.FieldConverter(FileHelpers.ConverterKind.Date, "ddMMyyyy")]
        public DateTime AddedDate;
    }

    [FileHelpers.DelimitedRecord(";")]
    public class SemicolonCustomer : BaseCustomer
    {
    }

    [FileHelpers.DelimitedRecord(",")]
    public class CommaCustomer : BaseCustomer
    {
    }

}

是否有可能在运行时编译一个子类

[FileHelpers.DelimitedRecord('"" + delimiter + "'")]
public class AnyDelimiterCustomer : BaseCustomer
{           
}

然后在代码中引用这个运行时编译的类

如何在FileHelpers中使用动态CSV分隔符

我刚刚意识到有一个DelimitedFileEngine可以用另一种方式解决您的问题。

你可以直接去

var engine = new DelimitedFileEngine(typeof(BaseCustomer));
engine.Options.Delimiter = ",";

似乎BaseCustomer需要用[DelimitedRecord]属性进行修饰,否则会引发异常,但分隔符会被提供给engine.Options.Delimiter的任何内容覆盖。

以下示例使用标记为条形分隔的格式导入逗号分隔的记录。

[DelimitedRecord("|")]
public class Format1
{
    public string Field1;           
    public string Field2;            
    public string Field3;            
    public string Field4;
}
static void Main(string[] args)
{
    var engine = new DelimitedFileEngine(typeof(Format1));
    // change the delimiter
    engine.Options.Delimiter = ","; 
    // import a comma separated record
    object[] importedObjects = engine.ReadString(@"a,b,c,d");
    foreach (object importedObject in importedObjects)
    {
        if (importedObject is Format1)
        {
            Format1 format1 = (Format1)importedObject;
            // process it (for example, check the values)
            Assert.AreEqual("a", format1.Field1);
            Assert.AreEqual("b", format1.Field2);
            Assert.AreEqual("c", format1.Field3);
            Assert.AreEqual("d", format1.Field4);
        }
    }
}

不,这是不可能的。

但是您可以使用FileHelper DelimitedClassBuilder来构建一个动态文件解析器,您可以在运行时设置分隔符:

DelimitedClassBuilder dcb = new DelimitedClassBuilder("Name", 
                                 "Here goes your col separator");
// You have to build your field definitions by hand now
dcb.AddField("FieldName", typeof(decimal));
...
// build the engine
DelimitedFileEngine fileEngine = new DelimitedFileEngine(dcb.CreateRecordClass());
// read the file
dynamic[] data = fileEngine.ReadFile(filePath);

您可以使用运行时类。你有两个选择。从字符串编译类

例如

// The class definition 
public string mClass =  
@"  
    [DelimitedRecord(""" + delimiter + @""")]
    public class BaseCustomer
    {
        public int CustId;
        public string Name;
        public decimal Balance;
        [FileHelpers.FieldConverter(FileHelpers.ConverterKind.Date, ""ddMMyyyy"")]
        public DateTime AddedDate;
    }
"; 
Type t = ClassBuilder.ClassFromString(mClass); 
FileHelperEngine engine = new FileHelperEngine(t); 
DataTable = engine.ReadFileAsDT("test.txt"); 

或者,也可以使用DelimitedClassBuilder类。

DelimitedClassBuilder cb = new DelimitedClassBuilder("BaseCustomer", delimiter);
cb.AddField("CustId", typeof(int));
cb.LastField.TrimMode = TrimMode.Both;
cb.LastField.FieldNullValue = 0;
cb.AddField("Balance", typeof(Decimal));
cb.AddField("AddedDate", typeof(DateTime));
engine = new FileHelperEngine(cb.CreateRecordClass());
DataTable dt = engine.ReadFileAsDT("test.txt");

这是可能的。但只能将序列化类型移动到单独的程序集中。

像这样:

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace FlaechenupdateScript
{

    static class Program
    {

        // http://www.codeproject.com/KB/cs/runtimecompiling.aspx
        private static System.Reflection.Assembly BuildAssembly(string code)
        {
            Microsoft.CSharp.CSharpCodeProvider provider =
               new Microsoft.CSharp.CSharpCodeProvider();
            System.CodeDom.Compiler.ICodeCompiler compiler = provider.CreateCompiler();
            System.CodeDom.Compiler.CompilerParameters compilerparams = new System.CodeDom.Compiler.CompilerParameters();
            string strLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
            string strBasePath = System.IO.Path.GetDirectoryName(strLocation);
            string strSerializationTypes = System.IO.Path.Combine(strBasePath, "SerializationTypes.dll");
            string strFileHelpersLocation = System.IO.Path.Combine(strBasePath, "FileHelpers.dll");
            compilerparams.ReferencedAssemblies.Add(strSerializationTypes);
            compilerparams.ReferencedAssemblies.Add(strFileHelpersLocation);
            compilerparams.GenerateExecutable = false;
            compilerparams.GenerateInMemory = true;
            System.CodeDom.Compiler.CompilerResults results =
               compiler.CompileAssemblyFromSource(compilerparams, code);
            if (results.Errors.HasErrors)
            {
                System.Text.StringBuilder errors = new System.Text.StringBuilder("Compiler Errors :'r'n");
                foreach (System.CodeDom.Compiler.CompilerError error in results.Errors)
                {
                    errors.AppendFormat("Line {0},{1}'t: {2}'n",
                           error.Line, error.Column, error.ErrorText);
                }
                throw new Exception(errors.ToString());
            }
            else
            {
                return results.CompiledAssembly;
            }
        } // End Function BuildAssembly

        public static Type GetClassType(Type tt, string strDelimiter)
        {
            string strFullTypeName = tt.FullName;
            string strTypeUniqueName = System.Guid.NewGuid().ToString() + System.Guid.NewGuid().ToString() + System.Guid.NewGuid().ToString() + System.Guid.NewGuid().ToString();
            strTypeUniqueName = "_" + strTypeUniqueName.Replace("-", "_");
            string xx = @"
            namespace CrapLord
            {
                [FileHelpers.IgnoreFirst]
                [FileHelpers.IgnoreEmptyLines]
                [FileHelpers.DelimitedRecord(""" + strDelimiter + @""")]
                public class " + strTypeUniqueName + @" : " + strFullTypeName + @"
                {
                }
            }
            ";
            System.Reflection.Assembly a = BuildAssembly(xx);
            var o = a.CreateInstance("CrapLord." + strTypeUniqueName);
            Type t = o.GetType();
            //System.Reflection.MethodInfo mi = t.GetMethod("EvalCode");
            //var s = mi.Invoke(o, null);
            return t;
        }

        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //Application.EnableVisualStyles();
            //Application.SetCompatibleTextRenderingDefault(false);
            //Application.Run(new Form1());
            Type t = GetClassType(typeof(Tools.Serialization.CSV.Customer), ",");
            //FileHelpers.FileHelperEngine engine = new FileHelpers.FileHelperEngine(typeof(SemicolonCustomer));
            FileHelpers.FileHelperEngine engine = new FileHelpers.FileHelperEngine(t);
            string str = "path/to/datafile";
            Tools.Serialization.CSV.Customer[] custs = (Tools.Serialization.CSV.Customer[])engine.ReadFile(str);
            //Customer[] custs = (Customer[]) engine.ReadFile("yourfile.txt");

            foreach (Tools.Serialization.CSV.Customer cli in custs)
            {
                Console.WriteLine();
                Console.WriteLine("Customer: " + cli.CustId.ToString() + " - " + cli.Name);
                Console.WriteLine("Added Date: " + cli.AddedDate.ToString("d-M-yyyy"));
                Console.WriteLine("Balance: " + cli.Balance.ToString());
                Console.WriteLine();
                Console.WriteLine("-----------------------------");
            } // Next cli

            Console.WriteLine(Environment.NewLine);
            Console.WriteLine(" --- Press any key to continue --- ");
            Console.ReadKey();
        }

    }

}

SerializationTypes程序集:

using System;
using System.Collections.Generic;
using System.Text;

namespace Tools.Serialization.CSV
{
    //------------------------
    //   RECORD CLASS (Example, change at your will)
    //   TIP: Remember to use the wizard to generate this class
    public class Customer
    {
        public int CustId;
        public string Name;
        public decimal Balance;
        [FileHelpers.FieldConverter(FileHelpers.ConverterKind.Date, "ddMMyyyy")]
        public DateTime AddedDate;
    }

}

也许您想使用Microsoft.VisualBasic.FileIO Namespace:中的TextFieldParser

string[] fields;
string[] delimiter = new string[] { "|" };
using (Microsoft.VisualBasic.FileIO.TextFieldParser parser =
           new Microsoft.VisualBasic.FileIO.TextFieldParser(filename))
{
    parser.Delimiters = delimiter;
    parser.HasFieldsEnclosedInQuotes = false;
    while (!parser.EndOfData)
    {
        fields = parser.ReadFields();
        //Do what you need
    }
}

添加强制转换为我解决了问题(FileHelpers V3.5.1)

var engine=新的DelimitedFileEngine(typeof(BaseCustomer));((FileHelpers.Options.DimitedRecordOptions)engine.Options).Dimiter=";