String.Substring相对于其他字符串处理方法有多快?

Joh*_*ant 4 .net string performance

我正在使用VB.NET处理一个长固定长度的记录.最简单的选项似乎是将整个记录加载到一个字符串中,并使用Substring按位置和长度访问字段.但似乎在每个调用中都会在Substring方法中进行一些冗余处理.这让我想知道使用基于流或阵列的方法是否可以获得更好的结果.

内容以包含UTF8字符数据的字节数组开始.我想到的其他几种方法如下所示.

  1. 将字符串加载到StringReader中并一次读取它的块
  2. 将字节数组转换为char数组并在数组中定位访问字符
  3. (这个看起来很愚蠢,但我会把它扔出去)将字节数组复制到内存流并使用StreamReader

绝对是不成熟的优化; 子串方法即使慢几毫秒也可以完全接受.但我想在编码之前我会问,只是为了看看是否有人能想到使用其他方法之一的理由.

Col*_*ett 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;
}
Run Code Online (Sandbox Code Playgroud)

现在到达那里(注意事实并非如此Substring()),它必须经历5次长度检查等.

如果您多次引用相同的子字符串,则可能值得将所有内容拉出一次并丢弃巨型字符串.您将在阵列中产生开销以存储所有这些子串.

如果它通常是"一次性"访问,那么Substring它,否则考虑分区.也许System.Data.DataTable会有用吗?如果您正在进行多次访问并解析为其他数据类型,那么DataTable对我来说看起来更具吸引力.如果您一次只需要一条记录在内存中,则a 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);
Run Code Online (Sandbox Code Playgroud)

如果反射适合你的想象:

[RecordLength(80)]
public class MyRecord
{
    [RecordFieldOffset(0)]
    string Name;

    [RecordFieldOffset(48)]
    int Age;

    [RecordFieldOffset(58)]
    Guid Systemid;
}
Run Code Online (Sandbox Code Playgroud)

只需运行属性,您就可以PropertyInfo.PropertyType了解如何处理记录中的子字符串; 你可以从属性中提取偏移量和总长度; 并使用填充的数据返回类的实例.从本质上讲,您可以使用反射来提取信息,以便从我之前的建议中调用RecordParser.AddField()和RecordLength().

然后将它全部包装成一个整洁的小小的,没有大惊小怪的课程:

RecordParser<MyRecord> r = new RecordParser<MyRecord>();
MyRecord data = r.Parse(recordString);
Run Code Online (Sandbox Code Playgroud)

甚至可以调用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
}
Run Code Online (Sandbox Code Playgroud)