C# 有人知道一种更快的方法来执行String.Split()吗?
我正在读取CSV文件的每一行,需要获取每列中的各个值。所以现在我只是使用:C# 有人知道一种更快的方法来执行String.Split()吗?,c#,.net,performance,string,csv,C#,.net,Performance,String,Csv,我正在读取CSV文件的每一行,需要获取每列中的各个值。所以现在我只是使用: values = line.Split(delimiter); 其中,行是一个字符串,它包含由分隔符分隔的值 在衡量我的ReadNextRow方法的性能时,我注意到它在String.Split上花费了66%,所以我想知道是否有人知道一种更快的方法来实现这一点 谢谢 需要指出的是,split()对于解析CSV文件是一种值得怀疑的方法,以防在文件中遇到逗号,例如: 1,"Something, with a comma",2
values = line.Split(delimiter);
其中,行
是一个字符串,它包含由分隔符分隔的值
在衡量我的ReadNextRow
方法的性能时,我注意到它在String.Split
上花费了66%,所以我想知道是否有人知道一种更快的方法来实现这一点
谢谢 需要指出的是,
split()
对于解析CSV文件是一种值得怀疑的方法,以防在文件中遇到逗号,例如:
1,"Something, with a comma",2,3
我要指出的另一件事是,在不知道您是如何分析的情况下,要小心分析这种低级细节。Windows/PC计时器的粒度可能会起作用,并且在循环过程中可能会有很大的开销,因此请使用某种控制值
这就是说,split()
是为处理正则表达式而构建的,正则表达式显然比您需要的复杂(而且处理转义逗号的工具是错误的)。另外,split()
会创建许多临时对象
因此,如果您想加快速度(我很难相信这部分的性能确实是一个问题),那么您需要手动完成,并且您需要重用缓冲区对象,这样您就不会不断地创建对象,也不会让垃圾收集器在清理它们时进行工作
其算法相对简单:
- 停在每一个逗号李>
- 当你点击引号时,继续直到你点击下一组引号李>
- 处理转义引号(即“\”)和转义逗号(\,)
顺便说一句,虽然这与Java有关,但它涉及到一般正则表达式的性能(这是通用的)和
replaceAll()
与手工编码的循环:。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
}
}
公共静态IEnumerable GetSplit(此字符串为s,字符为c)
{
int l=s.长度;
int i=0,j=s.IndexOf(c,0,l);
if(j==-1)//没有这样的子字符串
{
返回s;//返回原稿并断开
屈服断裂;
}
而(j!=-1)
{
如果(j-i>0)//非空?
{
返回s.Substring(i,j-i);//返回非空匹配
}
i=j+1;
j=s.IndexOf(c,i,l-i);
}
如果(i
上面的方法不一定比string.Split快。对于小字符串,它会在找到结果时返回结果,这就是延迟求值的威力。如果您有很长的行或需要节省内存,这就是方法
上面的方法受到IndexOf和Substring性能的限制,这两种方法做了太多的超出范围检查的索引,为了更快,您需要优化这些方法并实现自己的帮助器方法。您可以击败字符串。拆分性能,但需要CLIVER int hacking。您可以阅读我的文章。您可以假设该String.Split将接近最佳状态;也就是说,可能很难对其进行改进。到目前为止,更简单的解决方案是检查是否需要拆分字符串。很可能您将直接使用单个字符串。如果您定义StringShim类(引用字符串、开始和结束索引)您可以将字符串拆分为一组垫片。这些垫片的尺寸较小且固定,不会导致字符串数据复制。您可能认为需要进行优化,但实际情况是,您将在其他地方支付这些垫片的费用 例如,您可以“自己”进行拆分,遍历所有字符并处理遇到的每一列,但从长远来看,您将复制字符串的所有部分 < >我们可以在C或C++中做的一个优化,就是用“0”字符替换所有的分隔符,并将指针保持在列的起始位置。然后,我们不必复制所有的字符串数据,只是为了得到它的一部分。但是这不能在C语言中完成,也不想。 如果源代码中的列数与您需要的列数之间存在很大差异,那么手动遍历字符串可能会产生一些好处。但是,这一好处会花费您开发和维护它的时间 有人告诉我,90%的CPU时间都花在了10%的代码上。这个“事实”有很多不同。在我看来,如果你的应用程序需要处理CSV,那么将66%的时间花在Split上也没那么糟糕 戴夫 不过,我们讨论的是在非常大的字符串上实现ms节省。String.Split的主要问题(?)是它的通用性,因为它满足了许多需求 如果您比Split更了解您的数据,则可以改进您自己的数据 例如,如果:
private static final String[] fSplit(String src, char delim) {
ArrayList<String> output = new ArrayList<String>();
int index = 0;
int lindex = 0;
while((index = src.indexOf(delim,lindex)) != -1) {
output.add(src.substring(lindex,index));
lindex = index+1;
}
output.add(src.substring(lindex));
return output.toArray(new String[output.size()]);
}
private static final String[] fSplit(String src, String delim) {
ArrayList<String> output = new ArrayList<String>();
int index = 0;
int lindex = 0;
while((index = src.indexOf(delim,lindex)) != -1) {
output.add(src.substring(lindex,index));
lindex = index+delim.length();
}
output.add(src.substring(lindex));
return output.toArray(new String[output.size()]);
}
"First Name","Last Name","Address","Town","Postcode"
David,O'Leary,"12 Acacia Avenue",London,NW5 3DF
June,Robinson,"14, Abbey Court","Putney",SW6 4FG
Greg,Hampton,"",,
Stephen,James,"""Dunroamin"" 45 Bridge Street",Bristol,BS2 6TG
public int Split(string value, char separator)
{
int resultIndex = 0;
int startIndex = 0;
// Find the mid-parts
for (int i = 0; i < value.Length; i++)
{
if (value[i] == separator)
{
this.buffer[resultIndex] = value.Substring(startIndex, i - startIndex);
resultIndex++;
startIndex = i + 1;
}
}
// Find the last part
this.buffer[resultIndex] = value.Substring(startIndex, value.Length - startIndex);
resultIndex++;
return resultIndex;
var splitter = new StringSplitter(2);
splitter.Split("Hello World", ' ');
if (splitter.Results[0] == "Hello" && splitter.Results[1] == "World")
{
Console.WriteLine("It works!");
}
var splitter = new StringSplitter(2);
var len = splitter.Split("Hello World", ' ');
for (int i = 0; i < len; i++)
{
Console.WriteLine(splitter.Results[i]);
}
Public Shared Function FastSplit(inputString As String, separator As String) As String()
Dim kwds(1) As String
Dim k = 0
Dim tmp As String = ""
For l = 1 To inputString.Length - 1
tmp = Mid(inputString, l, 1)
If tmp = separator Then k += 1 : tmp = "" : ReDim Preserve kwds(k + 1)
kwds(k) &= tmp
Next
Return kwds
End Function
Public Shared Function FastSplit(inputString As String, separator As String) As String()
Dim sw As New Stopwatch
sw.Start()
Dim kwds(1) As String
Dim k = 0
Dim tmp As String = ""
For l = 1 To inputString.Length - 1
tmp = Mid(inputString, l, 1)
If tmp = separator Then k += 1 : tmp = "" : ReDim Preserve kwds(k + 1)
kwds(k) &= tmp
Next
sw.Stop()
Dim fsTime As Long = sw.ElapsedTicks
sw.Start()
Dim strings() As String = inputString.Split(separator)
sw.Stop()
Debug.Print("FastSplit took " + fsTime.ToString + " whereas split took " + sw.ElapsedTicks.ToString)
Return kwds
End Function
public static unsafe List<string> SplitString(char separator, string input)
{
List<string> result = new List<string>();
int i = 0;
fixed(char* buffer = input)
{
for (int j = 0; j < input.Length; j++)
{
if (buffer[j] == separator)
{
buffer[i] = (char)0;
result.Add(new String(buffer));
i = 0;
}
else
{
buffer[i] = buffer[j];
i++;
}
}
buffer[i] = (char)0;
result.Add(new String(buffer));
}
return result;
}
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;