有没有人知道更快的方法来做String.Split()?

19 .net c# csv string performance

我正在读取CSV文件的每一行,需要获取每列中的各个值.所以现在我只是使用:

values = line.Split(delimiter);
Run Code Online (Sandbox Code Playgroud)

where line是一个字符串,它包含分隔符分隔的值.

测量我的ReadNextRow方法的性能我注意到它花了66%String.Split,所以我想知道是否有人知道更快的方法来做到这一点.

谢谢!

Joh*_*ren 21

string.Split的BCL实现实际上非常快,我在这里做了一些测试,试图预先形成它并不容易.

但是有一件事你可以做,那就是将它作为一个生成器来实现:

public static IEnumerable<string> GetSplit( this string s, char c )
{
    int l = s.Length;
    int i = 0, j = s.IndexOf( c, 0, l );
    if ( j == -1 ) // No such substring
    {
        yield return s; // Return original and break
        yield break;
    }

    while ( j != -1 )
    {
        if ( j - i > 0 ) // Non empty? 
        {
            yield return s.Substring( i, j - i ); // Return non-empty match
        }
        i = j + 1;
        j = s.IndexOf( c, i, l - i );
    }

    if ( i < l ) // Has remainder?
    {
        yield return s.Substring( i, l - i ); // Return remaining trail
    }
}
Run Code Online (Sandbox Code Playgroud)

对于小字符串,上面的方法不一定比string.Split更快,但它会在找到它们时返回结果,这是懒惰评估的强大功能.如果你排长队或需要节省内存,这就是你要走的路.

上面的方法受到IndexOf和Substring的性能的限制,它执行过多的范围检查索引,并且需要更快地优化它们并实现自己的辅助方法.你可以击败string.Split性能,但它会采取切割器int-hacking.你可以在这里阅读我的帖子.

  • 我做了类似的方法,它比使用Split的现有算法慢,但因为我们处理这么大的字符串(多兆字节),它节省了大约30%的RAM消耗. (3认同)

cle*_*tus 18

应该指出的split()是,如果您在文件中遇到逗号,则解析CSV文件是一种可疑的方法,例如:

1,"Something, with a comma",2,3
Run Code Online (Sandbox Code Playgroud)

在不知道你如何分析的情况下,我要指出的另一件事是小心分析这种低级细节.Windows/PC计时器的粒度可能会发挥作用,您可能在循环中有很大的开销,因此使用某种控制值.

话虽这么说,它split()是为处理正则表达式而构建的,这些正则表达式显然比你需要的更复杂(而且无论如何都是处理转义逗号的错误工具).此外,split()还会创建大量临时对象.

因此,如果你想加快它(我很难相信这部分的性能确实是一个问题)那么你想要手工完成它并且你想重用你的缓冲区对象,所以你不是经常创建对象和给予垃圾收集器的工作是清理它们.

该算法相对简单:

  • 停在每个逗号;
  • 当你点击引号继续,直到你达到下一组报价;
  • 处理转义引号(即\")并且可以说是转义逗号(\,).

哦,为了让你对正则表达式的成本有所了解,有一个问题(Java不是C#,但原理是相同的),有人想用字符串替换每个第n个字符.我建议replaceAll()在String上使用.Jon Skeet手动编码循环.出于好奇,我对两个版本进行了比较,他的数量提高了一个数量级.

所以,如果你真的想要表现,那就是时候解析了.

或者,更好的是,使用其他人的优化解决方案,如快速CSV阅读器.

顺便说一句,虽然这与Java有关,但它涉及正则表达式(通用)和replaceAll()手动编码循环的性能:将char放入每个N个字符的java字符串中.


Ste*_*scu 8

这是一个使用 ReadOnlySpan 的非常基本的示例。在我的机器上,这大约需要 150 纳秒,而 string.Split() 大约需要 250 纳秒。40% 的改进就已经很不错了。

string serialized = "1577836800;1000;1";
ReadOnlySpan<char> span = serialized.AsSpan();

Trade result = new Trade();

index = span.IndexOf(';');
result.UnixTimestamp = long.Parse(span.Slice(0, index));
span = span.Slice(index + 1);

index = span.IndexOf(';');
result.Price = float.Parse(span.Slice(0, index));
span = span.Slice(index + 1);

index = span.IndexOf(';');
result.Quantity = float.Parse(span.Slice(0, index));

return result;
Run Code Online (Sandbox Code Playgroud)

请注意,ReadOnlySpan.Split() 很快将成为框架的一部分。请参阅 https://github.com/dotnet/runtime/pull/295