将数据从固定长度文件读取到类对象中

2023-12-05

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

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

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

我尝试实现此功能,但找不到 Layout() 属性。

Thanks.

固定长度文件示例:

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

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

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

我可以在这里给出一个实现示例(这只是一个示例,在生产使用之前对其进行编辑......):

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将数据从固定长度文件读取到类对象中 的相关文章