将固定长度文件中的数据读入类对象

时间:2014-09-26 13:11:37

标签: c# .net filereader fixed-length-file

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

在下面的链接中,有一个用户回答了这个类似的问题,但没有详细说明:

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

2 个答案:

答案 0 :(得分:9)

我不知道您的数据是如何序列化的(您没有指定任何协议或数据描述);但是你说对另一个问题的解决方案的详细说明将解决你的问题。我将为您详细说明:您可以轻松更改我的实现,以便根据您的格式解析数据(而不是使用二进制流,就像我在下面的示例中所做的那样)。 / p>

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

我可以举一个实现示例(它只是一个例子,在生产使用之前编辑它......):

包含您的数据结构的文件:

//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)

答案 1 :(得分:0)

如果结构形式良好,我很想创建一系列模拟文件结构的Reader(Stream)类。使用像Unity这样的IOC容器,您可以将文件流传递到顶层&#34; Document&#34;读者类,并允许它将流传递给&#34; child&#34;读者阅读文件的每个组成部分。作为每个逻辑&#34;记录&#34;完成后,您可以将事件/回调引发到数据库写入堆栈,以将表示文件的内存中对象图转换为数据库更新机制(可能需要进一步转换,或者只需要类似Mongo的文档写入)。