C#,读取固定宽度记录,在一个文件中改变记录类型

时间:2010-07-03 14:47:46

标签: c# .net-3.5 oop fixed-width

首先,我想澄清一点,我不是非常精通C#。在那个,我正在使用.Net 3.5在C#中工作的项目让我构建了一个类来读取和导出包含基于记录类型的多种固定宽度格式的文件。

目前,文件的每一行中的第一个字符位置指示5种类型的记录,表示特定的行格式。我遇到的问题是类型彼此不同。

Record type 1 has 5 columns, signifies beginning of the file

Record type 3 has 10 columns, signifies beginning of a batch
Record type 5 has 69 columns, signifies a transaction
Record type 7 has 12 columns, signifies end of the batch, summarizes
(these 3 repeat throughout the file to contain each batch)

Record type 9 has 8 columns, signifies end of the file, summarizes

这些固定宽度的文件是否有一个好的库?我已经看到一些好的想要将整个文件作为一个规范加载但不会这样做。

这些文件中大约有250个是在每个月末读取的,并且平均组合文件大小约为300兆。在这个项目中,效率对我来说非常重要。

根据我对数据的了解,我构建了一个我认为“对象应该是”的类层次结构......

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

namespace Extract_Processing
{
    class Extract
    {
        private string mFilePath;
        private string mFileName;
        private FileHeader mFileHeader;
        private FileTrailer mFileTrailer;
        private List<Batch> mBatches;       // A file can have many batches

        public Extract(string filePath)
        { /* Using file path some static method from another class would be called to parse in the file somehow */ }

        public string ToString()
        { /* Iterates all objects down the heiarchy to return the file in string format */ }

        public void ToFile()
        { /* Calls some method in the file parse static class to export the file back to storage somewhere */ }
    }

    class FileHeader
    { /* ... contains data types for all fields in this format, ToString etc */ }

    class Batch
    {
        private string mBatchNumber;                // Should this be pulled out of the batch header to make LINQ querying simpler for this data set?
        private BatchHeader mBatchHeader;
        private BatchTrailer mBatchTrailer;
        private List<Transaction> mTransactions;    // A batch can have multiple transactions

        public string ToString()
        { /* Iterates through batches to return what the entire batch would look like in string format */ }
    }

    class BatchHeader
    { /* ... contains data types for all fields in this format, ToString etc */ }

    class Transaction
    { /* ... contains data types for all fields in this format, ToString etc */ }

    class BatchTrailer
    { /* ... contains data types for all fields in this format, ToString etc */ }

    class FileTrailer
    { /* ... contains data types for all fields in this format, ToString etc */ }

}

我遗漏了许多构造函数和其他方法,但我认为这个想法应该非常可靠。我正在寻找对我正在考虑的方法的想法和批评,对C#不了解,执行时间是最高优先级。

除了一些批评之外最大的问题是,我该如何引入这个文件?我用其他语言引入了很多文件,比如使用FSO方法的VBA,Microsoft Access ImportSpec来读取文件(5次,每个规格一个......哇效率低!),创建了一个'Cursor'对象视觉foxpro(这是FAAAAAAAST,但又必须做五次),但如果说存在的话,我正在C#中寻找隐藏的宝石。

感谢您阅读我的小说,请告诉我您是否有理解它的问题。我正在周末去看看这个设计,看看我是否买了它,并希望通过这种方式来实现它。

4 个答案:

答案 0 :(得分:4)

FileHelpers很好。它有一些缺点,它似乎不再处于活动开发状态,它使您可以在字段中使用公共变量,而不是让您使用属性。但其他方面都不错。

你在用这些文件做什么?您是否将它们加载到SQL Server中?如果是这样,你正在寻找快速和简单,我建议这样的设计:

  1. 在数据库中创建与5种记录类型中的每种记录对应的登台表。请考虑添加LineNumber列和FileName列,以便将问题跟踪回文件本身。
  2. 逐行读取文件并将其解析为业务对象,或直接解析为与表对应的ADO.NET DataTable对象。
  3. 如果您使用了业务对象,请应用数据转换或业务规则,然后将数据放入与您的表对应的DataTable对象中。
  4. 每个DataTable到达适当的BatchSize(比如说1000条记录)后,使用SqlBulkCopy对象将数据泵入临时表。在每次SqlBulkCopy操作之后,清除DataTable并继续处理。
  5. 如果您不想使用业务对象,请在SQL Server中进行任何最终数据操作。
  6. 你可以用500多行C#完成整个事情。

答案 1 :(得分:2)

除了一些批评之外,最大的问题是,我该如何引入此文件?

我不知道文件IO有什么好的库,但阅读非常简单。

使用64kB缓冲区实例化StreamReader class以限制磁盘IO操作(我的估计是每月末每个文件平均1500个事务)。

现在您可以流式传输文件:
1)在每行的开始处使用Read来确定记录的类型 2)使用ReadLine方法和String.Split方法获取列值 3)使用列值创建对象。

您可以手动缓冲来自Stream的数据,并IndexOf + SubString以获得更高的性能(如果操作正确)。

此外,如果行不是列而是二进制格式的原始数据类型,则可以使用BinaryReader class以非常简单且高效的方式读取对象。

答案 2 :(得分:1)

我的一个批评是你没有正确实现ToString。

    public string ToString()

应该是:

    public override string ToString()

答案 3 :(得分:-1)

这类事情的最佳图书馆是filehelpers