我正在使用VB.NET处理一个长固定长度的记录。最简单的选项似乎是将整个记录加载到一个字符串中,并使用Substring按位置和长度访问字段。但似乎在每个调用中都会在Substring方法中进行一些冗余处理。这让我想知道我是否可以使用基于流或阵列的方法获得更好的结果。
内容以包含UTF8字符数据的字节数组开头。我想到的其他几种方法如下所示。
肯定是过早优化;子串方法即使慢几毫秒也可以完全接受。但我想在编码之前我会问,只是为了看看是否有人能想到使用其他方法之一的理由。
答案 0 :(得分:6)
子字符串的主要成本是将子字符串切换为新字符串。使用Reflector,您可以看到:
private unsafe string InternalSubString(int startIndex, int length, bool fAlwaysCopy)
{
if (((startIndex == 0) && (length == this.Length)) && !fAlwaysCopy)
{
return this;
}
string str = FastAllocateString(length);
fixed (char* chRef = &str.m_firstChar)
{
fixed (char* chRef2 = &this.m_firstChar)
{
wstrcpy(chRef, chRef2 + startIndex, length);
}
}
return str;
}
现在到达那里(注意那不是Substring()
)它必须经过长度等5次检查。
如果您多次引用相同的子字符串,则可能值得将所有内容拉出一次并倾倒巨型字符串。您将在数组中产生开销以存储所有这些子字符串。
如果它通常是“一次性”访问,那么Substring它,否则考虑分区。也许System.Data.DataTable
会有用吗?如果您正在进行多次访问并解析为其他数据类型,那么DataTable
对我来说更具吸引力。如果您一次只需要一条记录在内存中,则Dictionary<string,object>
应足以容纳一条记录(字段名称必须是唯一的)。
或者,您可以编写一个自定义的泛型类来处理固定长度的记录读取。指示每个字段的起始索引和字段的类型。字段的长度由下一个字段的开头推断(例外是可以从总记录长度推断出的最后一个字段)。可以使用int.Parse()
,double.Parse()
,bool.Parse()
等内容自动转换类型。
RecordParser r = new RecordParser();
r.AddField("Name", 0, typeof(string));
r.AddField("Age", 48, typeof(int));
r.AddField("SystemId", 58, typeof(Guid));
r.RecordLength(80);
Dictionary<string, object> data = r.Parse(recordString);
如果反思适合你的想法:
[RecordLength(80)]
public class MyRecord
{
[RecordFieldOffset(0)]
string Name;
[RecordFieldOffset(48)]
int Age;
[RecordFieldOffset(58)]
Guid Systemid;
}
只需运行属性,您就可以让PropertyInfo.PropertyType
知道如何处理记录中的子字符串;你可以从属性中提取偏移量和总长度;并使用填充的数据返回类的实例。从本质上讲,您可以使用反射来提取信息,以便从我之前的建议中调用RecordParser.AddField()和RecordLength()。
然后将它全部包装成一个整洁的小课程:
RecordParser<MyRecord> r = new RecordParser<MyRecord>();
MyRecord data = r.Parse(recordString);
甚至可以到目前为止调用r.EnumerateFile("path\to\file")
并使用yield return
枚举语法来解析记录
RecordParser<MyRecord> r = new RecordParser<MyRecord>();
foreach (MyRecord data in r.EnumerateFile("foo.dat"))
{
// Do stuff with record
}
答案 1 :(得分:3)
最快的方法可能是使用流技术,因为假设您可以按顺序读取每个字段,它只会保留您在内存中所需的内容remembers where you are in the process。
答案 2 :(得分:1)
你尝试做什么听起来像解析任务。如果我理解正确,你加载一个包含多个字段及其值的巨大字符串。对于这种特殊情况,Substring不会特别高效。对于每个字段及其值,您将需要在较大的字符串中调用具有特定位置和长度的Substring。这是相当多的开销。
作为替代方案,您可以实现一个简单的解析器,它将从头到尾处理您的字符串一次,并在一次传递中检索每个字段和值。这样的解析器不需要非常复杂......只需要一个简单的1字符前瞻解析器。您可能甚至不需要对输入进行标记化...您可以以流式方式处理它以提取一个字段,然后将其值,将其粘贴在某个容器中,然后继续。
如果输入字符串比一系列字段和值(即其结构化)更复杂,则可能需要更复杂的解析器。有很多工具,比如antler,它们提供的框架可以为您生成语法,生成解析器,并提供一个很好的API来使用您的解析内容。
答案 3 :(得分:0)
你是如何首先阅读唱片的?
你是逐字逐句阅读吗?
你可以在阅读时动态地做事,因此不会涉及子字符串。
如果你必须阅读一次,然后处理然后读入一个字符串并使用StringReader,它将允许你逐个字符或多个字符读取。