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

고정 길이 파일은 어떻게 생겼습니까? 우리가 무엇을 제안하기 전에 내용을 엿볼 수 있습니까? –

+0

@theghostofc 지금 StreamReader를 구현하고 나중에 spilled 또는 indexof와 같은 간단한 문자열 함수를 사용하여 데이터를 얻으려고합니다. 그러나이 모든 것이 건너 뛸 수 있는지 궁금합니다. 위에 제공된 링크 중 하나와 비슷하지만, 코드가 훨씬 깨끗해 보일 것입니다. 제발 여기에 코드를 요구하지 않고 단지이 경우에 어떤 옵션이 있는지 알고 싶지는 마십시오. – IFlyHigh

+0

고정 길이 파일 샘플에 대해서도 내 질문을 편집했습니다. – IFlyHigh

답변

0

구조가 잘 형성되면, 나는의 ... 리더 일련의 (스트림) 파일의 구조를 모방하는 클래스를 생성하는 유혹 것입니다. Unity와 같은 IOC 컨테이너를 사용하면 파일 스트림을 최상위 "Document"리더 클래스에 전달할 수 있으며 스트림을 "하위"독자에게 전달하여 파일의 각 구성 요소를 읽을 수 있습니다. 각 논리적 "레코드"가 완료되면 데이터베이스 쓰기 스택에 이벤트/콜백을 발생시켜 파일을 나타내는 메모리 내 객체 그래프를 데이터베이스 업데이트 메커니즘으로 변환 할 수 있습니다 (변환이 더 필요할 수도 있고 단순히 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); 
     } 
    } 
} 

당신은 예를 들어 바이너리 파일에 대해이 코드를 테스트하려면 다음 16 진수 코드를 가진 파일을 만들 수 있습니다

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 바이트)

(16 진수 코드를 추가하고 testFile.bin으로 저장 한 파일을 만들었습니다.)

+0

샘플 코드 (및 이메일)을 보내 주셔서 감사합니다. 필자가 Generics, Compiled Expression Trees 및 Convert.ChangeType을 사용하여 좀 더 일반적인 버전의 답변을 작성 했으므로 새로운 대답을 게시하지는 않았지만이 블로그 게시물을 읽고 http : //terryaney.wordpress.com/2014/10/01/fixedwidthstreamreader-conceived-from-death/. – Terry

+0

이 솔루션은 머리글/바닥 글 유스 케이스를 처리하지 못하는 것 같습니까? Visual Basic의'TextFieldParser' 클래스를 사용하여'SetFieldWidths'를 호출하여 파일을 읽기 위해 "마크 업"할 수는 있지만 머리말과 꼬리말의 존재는 상황을 복잡하게 만들 수 있습니다. 필자는 일반 구문 분석을 복잡하게 만드는 파일 전체에 여러 소문자 머리글/바닥 글 레코드가 분산되어있는 고정 길이 파일을 다루고 있습니다. – PeterX

+0

내 대답의 목적은 OP의 주요 문제에 대한 해결책을 제공하는 것이 었습니다. "_ 구현하려고 시도했지만 Layout() Attribute._"을 찾을 수 없습니다 (OP의 유일한 문제인 것으로 보입니다). 필자의 주요 목적은 속성 사용법의 예를 보여 주며 완벽한 구현을 제공하지 않는 것입니다 ("내 구현을 변경하여 사용자가 자신의 형식에 맞게 데이터를 분석 할 수있을 것입니다 _"). 나의 예제에서'MyData'는 헤더 변수에'Layout' 속성을 가지고 있고 바닥 글 변수에 대해서도 같은 헤더 섹션을 포함 할 수 있습니다. 당신의 유스 케이스에서 실현 가능합니까? – Giuseppe