2

我有一个固定长度的文件,想将其数据读入类对象。这些对象将进一步用于在数据库中插入/更新数据。虽然可以使用 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
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);
        }
    }
}

如果您想针对示例二进制文件测试该代码,您可以使用以下十六进制代码创建一个文件:

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)

于 2014-09-29T11:39:08.590 回答
0

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

于 2014-09-26T13:34:31.310 回答