我有一个固定长度的文件,并希望将其数据读入类对象。这些对象将进一步用于在数据库中插入/更新数据。虽然可以使用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
答案 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
表示以下字节:
(我使用XVI32创建了一个文件,添加了十六进制代码并将其另存为testFile.bin)
答案 1 :(得分:0)
如果结构形式良好,我很想创建一系列模拟文件结构的Reader(Stream)类。使用像Unity这样的IOC容器,您可以将文件流传递到顶层&#34; Document&#34;读者类,并允许它将流传递给&#34; child&#34;读者阅读文件的每个组成部分。作为每个逻辑&#34;记录&#34;完成后,您可以将事件/回调引发到数据库写入堆栈,以将表示文件的内存中对象图转换为数据库更新机制(可能需要进一步转换,或者只需要类似Mongo的文档写入)。