2012-04-15 103 views
6

这是从some other question的讨论衍生出来的。解析无字符串拆分

假设我必须解析大量很长的字符串。每个字符串都包含一个由空格分隔的double(当然用文本表示)的序列。我需要将double解析为List<double>

标准解析技术(使用string.Split + double.TryParse)似乎很慢:对于每个数字我们需要分配一个字符串。

我试图使它成为类似于C的方式:计算包含数字的子串的开始和结束的索引,并将其解析为“到位”,而不创建额外的字符串。 (见http://ideone.com/Op6h0,如下图所示的相关部分。)

int startIdx, endIdx = 0; 
while(true) 
{ 
    startIdx = endIdx; 
    // no find_first_not_of in C# 
    while (startIdx < s.Length && s[startIdx] == ' ') startIdx++; 
    if (startIdx == s.Length) break; 
    endIdx = s.IndexOf(' ', startIdx); 
    if (endIdx == -1) endIdx = s.Length; 
    // how to extract a double here? 
} 

还有就是string.IndexOf过载,只有一个给定的字符串中进行搜索,但我没能找到解析从子双重方法,实际上并没有提取那子串第一。

有没有人有想法?

+5

你有没有证明了这其实是一个瓶颈?我不知道*以任何方式做副手,但我肯定希望有一些证据表明这是微型优化之前的问题。 – 2012-04-15 11:22:29

+0

@Jon:不是。这个问题是基于链接问题的讨论(http://stackoverflow.com/questions/10053449/extract-numbers-from-string)。对不起。 – Vlad 2012-04-15 11:23:13

+0

够公平的。我怀疑手写的解析程序会比BCL团队提出的大概经过优化的大量体验方法要慢:) – 2012-04-15 11:25:23

回答

7

没有托管的API来解析子字符串中的一个double。我的猜测是,与double.Parse中的所有浮点运算相比,分配字符串将是微不足道的。

无论如何,您可以通过创建一个长度为100且仅包含空格的“缓冲区”字符串来保存分配。然后,对于要分析的每个字符串,使用不安全代码将字符复制到此缓冲区字符串中。用空格填充缓冲区字符串。而对于解析,您可以使用NumberStyles.AllowTrailingWhite,这将导致尾随空白被忽略。

获得一个指向字符串实际上是完全支持的操作:

string l_pos = new string(' ', 100); //don't write to a shared string! 
    unsafe 
    { 
     fixed (char* l_pSrc = l_pos) 
     {    
       // do some work 
     } 
    } 

C#有特殊的语法为字符串绑定到一个char *。

如果你想这样做真快
+0

我的理解是否正确:你的意思是修改一个假定不可变的'System.String'与不安全的代码? – Vlad 2012-04-15 12:23:20

+0

不会解析所有的空格,这使得每次都比分配一个新的字符串更慢吗? – svick 2012-04-15 12:28:22

+0

@弗拉德,是的,你可以做到这一点。只是不要传递该字符串,并保持私密。这样你就不会违反其他代码所做的假设。 StringBuilder在内部使用这种技术。当你使用StringBuilder时,它只是把你的内部缓冲区传给你。 StringBuilder.ToString通常是O(1)。 – usr 2012-04-15 12:37:49

2

,我会用一个状态机

这可能看起来像:

enum State 
{ 
    Separator, Sign, Mantisse etc. 
} 
State CurrentState = State.Separator; 
int Prefix, Exponent, Mantisse; 
foreach(var ch in InputString) 
{ 
    switch(CurrentState) 
    { // set new currentstate in dependence of ch and CurrentState 
     case Separator: 
      GotNewDouble(Prefix, Exponent, Mantisse); 


    } 

} 
+0

你的意思是手动解析,而不使用TryParse? – Vlad 2012-04-15 13:16:22

+0

是的,如果你正在使用TryParse,你需要每次都有一个新的字符串实例。那么你有相同的行为,如var values = string.Split('').Select(s => double.Parse(s))。ToArray(); – user287107 2012-04-15 13:22:51

+0

那么,手动解析通常是缓慢的,越野车,我想尽可能避免重新发明车轮。 – Vlad 2012-04-15 17:06:48