2014-09-26 102 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