Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 有人知道一种更快的方法来执行String.Split()吗?_C#_.net_Performance_String_Csv - Fatal编程技术网

C# 有人知道一种更快的方法来执行String.Split()吗?

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

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

values = line.Split(delimiter);
其中,
是一个字符串,它包含由分隔符分隔的值

在衡量我的
ReadNextRow
方法的性能时,我注意到它在
String.Split
上花费了66%,所以我想知道是否有人知道一种更快的方法来实现这一点


谢谢

需要指出的是,
split()
对于解析CSV文件是一种值得怀疑的方法,以防在文件中遇到逗号,例如:

1,"Something, with a comma",2,3
我要指出的另一件事是,在不知道您是如何分析的情况下,要小心分析这种低级细节。Windows/PC计时器的粒度可能会起作用,并且在循环过程中可能会有很大的开销,因此请使用某种控制值

这就是说,
split()
是为处理正则表达式而构建的,正则表达式显然比您需要的复杂(而且处理转义逗号的工具是错误的)。另外,
split()
会创建许多临时对象

因此,如果您想加快速度(我很难相信这部分的性能确实是一个问题),那么您需要手动完成,并且您需要重用缓冲区对象,这样您就不会不断地创建对象,也不会让垃圾收集器在清理它们时进行工作

其算法相对简单:

  • 停在每一个逗号
  • 当你点击引号时,继续直到你点击下一组引号
  • 处理转义引号(即“\”)和转义逗号(\,)
哦,为了让您了解正则表达式的成本,有一个问题(Java不是C#,但原理是一样的),有人想用字符串替换第n个字符on String。Jon Skeet手动对循环进行编码。出于好奇,我比较了这两个版本,他的版本要好一个数量级

所以,如果你真的想要性能,是时候手动解析了

或者,更好的办法是,像这样使用其他人的优化解决方案


顺便说一句,虽然这与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更了解您的数据,则可以改进您自己的数据

例如,如果:

  • 您不关心空字符串,因此不需要以任何特殊方式处理这些字符串
  • 你不需要修剪琴弦,所以你不需要在琴弦周围做任何事情
  • 您不需要检查引用的逗号或q
    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;