2

固定長のファイルがあり、そのデータをクラス オブジェクトに読み込みたいと考えています。これらのオブジェクトは、データベースにデータを挿入/更新するためにさらに使用されます。StreamReader で実行できますが、より洗練されたソリューションを探しています。FileHelper も別のソリューションですが、自分のプログラムでオープン ソース コードを使用したくありません。他のオプションはありますか?

以下のリンクでは、1 人のユーザーがこの同様の質問に回答していますが、詳しく説明されていません。

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

これを実装しようとしましたが、Layout() 属性が見つかりません。

ありがとう。

固定長ファイルのサンプル:

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

2 に答える 2

11

データがどのようにシリアル化されたかわかりません (プロトコルもデータの説明も指定していません)。ただし、他の質問の解決策を詳しく説明することで問題が解決するとおっしゃいました。そのための詳細を説明します。次の例で行ったように、バイナリストリームを使用する代わりに、データがフォーマットに従って解析されるように実装を変更するのは簡単です。

あなたが言及している質問では、解決策を得るために独自の属性を実装することを提案していたと思います。

ここで実装の例を挙げることができます(これは単なる例です。本番環境で使用する前に編集してください...):

データ構造を含むファイル:

//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 バイト)
  • double 0.5 (8 バイト)

(XVI32を使用してファイルを作成し、その16進コードを追加してtestFile.binとして保存しました)

于 2014-09-29T11:39:08.590 に答える
0

構造が適切に形成されている場合、ファイル構造を模倣する一連の ...Reader(Stream) クラスを作成したくなるでしょう。Unity のような IOC コンテナーを使用すると、ファイル ストリームを最上位の「ドキュメント」リーダー クラスに渡し、そのストリームを「子」リーダーに渡して、ファイルの各コンポーネントを読み取ることができます。各論理「レコード」が完了すると、データベース書き込みスタックへのイベント/コールバックを発生させて、ファイルを表すメモリ内オブジェクト グラフをデータベース更新メカニズムに変換できます (これには、さらに変換または単に Mongo が必要になる場合があります)。 -ドキュメントの書き込みのようなもの)。

于 2014-09-26T13:34:31.310 に答える