2014-09-26 105 views
1

我有一个固定长度的文件,并希望将其数据读入类对象。这些对象将进一步用于在数据库中插入/更新数据。虽然可以使用StreamReader完成,但我正在寻找更复杂的解决方案。 FileHelper是另一种解决方案,但我不想在我的程序中使用开源代码。有没有其他的选择?从固定长度文件读取数据到类对象中

在下面的链接,一个用户已经回答了这个类似的问题,但它没有详细阐述:

https://codereview.stackexchange.com/questions/27782/how-to-read-fixed-width-data-fields-in-net

我想实现这个,但我无法找到布局()属性。

谢谢。

样品固定长度文件:

aCSTDCECHEUR20140701201409161109 //Header of the file 
b0000000000050115844085700800422HB HERBOXAN-COMPACT WHITE 12,5L   0000002297P0000000184L0000000000 0000000000 
zCSTDCECH201409161109 148 //Footer of the file 
+0

您的固定长度文件是怎样的?在我们提出任何建议之前,我们可以先看看内容以及您尝试过的内容吗? – 2014-09-26 13:18:50

+0

@theghostofc现在我正在实现StreamReader,后来计划使用简单的字符串函数(如spilt或indexof)来获取数据..但是我想知道是否所有这些都可以跳过,就像我在上面提供的链接中那样,那么代码将看起来更干净。请不要在这里我不要求代码,只是想知道我在这种情况下有什么选择。 – IFlyHigh 2014-09-26 13:56:26

+0

编辑我的问题也为示例固定长度文件。 – IFlyHigh 2014-09-26 13:56:52

回答

0

如果结构良好的,我会忍不住创造了一系列的...读卡器(流),它模仿你的文件结构类。使用像Unity这样的IOC容器,您可以将文件流传递到顶级“文档”阅读器类,并允许它将流传递给“子”阅读器以读取文件的每个组件。当每个逻辑“记录”完成后,您可以向数据库写入堆栈提出一个事件/回调,以将表示文件的内存中对象图转换为数据库更新机制(这可能需要进一步转换,或者只是一个Mongo像文件写入)。

5

我不知道你的数据是如何序列化的(你没有指定任何协议或数据描述);但是你说过,为其他问题制定解决方案可以解决你的问题。我正在给你一个阐述:你可以很容易地改变我的实现,以便根据你的格式解析数据(而不是像下面的例子那样使用二进制流)。

我认为在您提到的问题中,他们建议实施自己的属性以获得解决方案。

我可以在这里给出一个实现的例子(这只是一个例子,在生产使用前编辑它...):包含你的数据结构

文件:

//MyData.cs 

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

namespace FixedLengthFileReader 
{ 
    class MyData 
    { 
     [Layout(0, 10)] 
     public string field1; 
     [Layout(10, 4)] 
     public int field2; 
     [Layout(14, 8)] 
     public double field3; 

     public override String ToString() { 
      return String.Format("String: {0}; int: {1}; double: {2}", field1, field2, field3); 
     } 
    } 
} 

属性:

// LayoutAttribute.cs 

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

namespace FixedLengthFileReader 
{ 
    [AttributeUsage(AttributeTargets.Field)] 
    class LayoutAttribute : Attribute 
    { 
     private int _index; 
     private int _length; 

     public int index 
     { 
      get { return _index; } 
     } 

     public int length 
     { 
      get { return _length; } 
     } 

     public LayoutAttribute(int index, int length) 
     { 
      this._index = index; 
      this._length = length; 
     } 
    } 
} 

阅读器实现的例子:

//FixedLengthReader.cs 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.IO; 
using System.Reflection; 

namespace FixedLengthFileReader 
{ 
    class FixedLengthReader 
    { 
     private Stream stream; 
     private byte[] buffer; 

     public FixedLengthReader(Stream stream) 
     { 
      this.stream = stream; 
      this.buffer = new byte[4]; 
     } 

     public void read<T>(T data) 
     { 
      foreach (FieldInfo fi in typeof(T).GetFields()) 
      { 
       foreach (object attr in fi.GetCustomAttributes()) 
       { 
        if (attr is LayoutAttribute) 
        { 
         LayoutAttribute la = (LayoutAttribute)attr; 
         stream.Seek(la.index, SeekOrigin.Begin); 
         if (buffer.Length < la.length) buffer = new byte[la.length]; 
         stream.Read(buffer, 0, la.length); 

         if (fi.FieldType.Equals(typeof(int))) 
         { 
          fi.SetValue(data, BitConverter.ToInt32(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(bool))) 
         { 
          fi.SetValue(data, BitConverter.ToBoolean(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(string))) 
         { 
          // --- If string was written using UTF8 --- 
          byte[] tmp = new byte[la.length]; 
          Array.Copy(buffer, tmp, tmp.Length); 
          fi.SetValue(data, System.Text.Encoding.UTF8.GetString(tmp)); 

          // --- ALTERNATIVE: Chars were written to file --- 
          //char[] tmp = new char[la.length - 1]; 
          //for (int i = 0; i < la.length; i++) 
          //{ 
          // tmp[i] = BitConverter.ToChar(buffer, i * sizeof(char)); 
          //} 
          //fi.SetValue(data, new string(tmp)); 
         } 
         else if (fi.FieldType.Equals(typeof(double))) 
         { 
          fi.SetValue(data, BitConverter.ToDouble(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(short))) 
         { 
          fi.SetValue(data, BitConverter.ToInt16(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(long))) 
         { 
          fi.SetValue(data, BitConverter.ToInt64(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(float))) 
         { 
          fi.SetValue(data, BitConverter.ToSingle(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(ushort))) 
         { 
          fi.SetValue(data, BitConverter.ToUInt16(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(uint))) 
         { 
          fi.SetValue(data, BitConverter.ToUInt32(buffer, 0)); 
         } 
         else if (fi.FieldType.Equals(typeof(ulong))) 
         { 
          fi.SetValue(data, BitConverter.ToUInt64(buffer, 0)); 
         } 
        } 
       } 
      } 
     } 
    } 
} 

最后方案实施的一个例子(很简单):

// Program.cs 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.IO; 

namespace FixedLengthFileReader 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MyData md = new MyData(); 
      Console.WriteLine(md); 

      Stream s = File.OpenRead("testFile.bin"); 
      FixedLengthReader flr = new FixedLengthReader(s); 
      flr.read(md); 
      s.Close(); 

      Console.WriteLine(md); 
     } 
    } 
} 

如果你想测试对一个例子二进制文件代码,你可以创建具有以下的十六进制代码的文件:

41 42 43 44 45 46 47 48 49 4A 01 00 00 00 00 00 00 
00 00 00 E0 3F 

它代表的字节:

  • 字符串ABCDEFGHIJ(10字节)
  • 整数1(4字节)
  • 双0.5(8字节)

(我使用XVI32创建了一个文件,添加了十六进制代码并将其保存为testFile.bin)

+0

感谢您的示例代码(和电子邮件)。鉴于我刚刚写了泛型,编译表达式树和Convert.ChangeType的答案稍微更通用的版本,我没有发布一个新的答案,但你可以阅读我的博客文章,并看到示例代码在http: //terryaney.wordpress.com/2014/10/01/fixedwidthstreamreader-conceived-from-death/。 – Terry 2014-10-01 19:00:37

+0

此解决方案似乎无法处理页眉/页脚用例?虽然可以使用Visual Basic的'TextFieldParser'类来调用'SetFieldWidths'来“标记”一个文件以供读取,页眉和页脚记录的存在会使事情变得复杂。我正在处理一个固定长度的文件,该文件在整个文件中扩展了多个小计页眉/页脚记录,这使得泛型解析变得复杂。 – PeterX 2015-02-09 05:03:57

+0

我的答案的目的是为OP的主要问题提供解决方案:“_I试图实现这一点,但我无法找到Layout()Attribute._”(在她的描述中,我认为这是OP的唯一问题)。我的主要目的是显示一个属性使用的例子,而不是提供一个完整的实现(正如我写的:“_it将很容易为您更改我的实现,以便根据您的格式解析数据”)。在我的例子中,MyData可以包含一个头部分,头部变量的'Layout'属性和脚注变量相同。在你的用例中可行吗? – Giuseppe 2015-02-10 11:09:37