2012-04-15 21 views
6

Dies ist ein Spin-off aus der Diskussion in some other question.Parse ohne String Split

Angenommen, ich muss eine große Anzahl sehr langer Strings parsen. Jede Zeichenfolge enthält eine Sequenz von double s (natürlich in Textdarstellung), die durch Leerzeichen voneinander getrennt sind. Ich muss die double s in eine List<double> analysieren.

Die Standard-Analyse-Technik (mit string.Split + double.TryParse) scheint ziemlich langsam zu sein: für jede der Zahlen müssen wir eine Zeichenfolge zuordnen.

Ich habe versucht, es zu einem alten C-ähnlichen Weg zu machen: Berechne die Indizes des Anfangs und des Endes von Teilstrings, die die Zahlen enthalten, und analysiere sie "an Ort und Stelle", ohne zusätzliche Zeichenketten zu erzeugen. (Siehe http://ideone.com/Op6h0, unter dem relevanten Teil gezeigt.)

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? 
} 

Es gibt eine Überlastung von string.IndexOf, nur innerhalb eines bestimmten Teilzeichens gesucht, aber ich konnte keine Methode finden, um eine Doppel von Teilzeichenfolge Parsen, ohne tatsächlich zu extrahieren, dass Teilzeichenfolge zuerst.

Hat jemand eine Idee?

+5

Haben Sie erwies sich dies tatsächlich ein Engpass? Ich weiß nicht, wie ich das machen könnte, aber ich würde sicherlich einige Beweise dafür haben, dass es ein Problem vor der Mikrooptimierung ist. –

+0

@ Jon: nicht wirklich. Die Frage basiert auf der Diskussion der verknüpften Frage (http://stackoverflow.com/questions/10053449/extract-numbers-from-string). Das tut mir leid. – Vlad

+0

Fair genug. Ich vermute, dass eine handgeschriebene Parser-Routine langsamer wäre als die vom BCL-Team ersonnene Methode der vermutlich-optimierten-mit-vielen-Erfahrungen :) –

Antwort

7

Es gibt keine verwaltete API, um ein Double von einem Teilstring zu analysieren. Meine Vermutung ist, dass die Zuweisung der Zeichenfolge im Vergleich zu allen Gleitkommaoperationen in double.Parse unbedeutend ist.

Wie auch immer, Sie können die Zuweisung speichern, indem Sie einen "Puffer" -String der Länge 100 erstellen, der nur aus Leerzeichen besteht. Anschließend kopieren Sie die Zeichen für jede Zeichenfolge, die Sie analysieren möchten, in diese Pufferzeichenfolge unter Verwendung des unsicheren Codes . Sie füllen die Pufferzeichenfolge mit Leerzeichen. Und zum Parsen können Sie NumberStyles.AllowTrailingWhite verwenden, was dazu führt, dass nachfolgende Leerzeichen ignoriert werden.

einen Zeiger auf Zeichenfolge zu erhalten ist tatsächlich eine vollständig unterstützte Operation:

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

C# spezielle Syntax hat eine Zeichenkette in einem char * zu binden.

+0

Verstehe ich richtig: Sie meinen, einen vermeintlich unveränderlichen 'System.String' mit unsicherem Code zu modifizieren? – Vlad

+0

Würden nicht alle Whitespaces syntaktisch analysiert werden, wäre dies tatsächlich langsamer als jedes Mal, wenn eine neue Zeichenfolge zugewiesen wird? – svick

+0

@Vlad, ja du kannst das tun. Übergeben Sie diese Zeichenfolge einfach nicht und lassen Sie sie privat. Auf diese Weise verletzen Sie nicht Annahmen, die anderer Code macht. StringBuilder verwendet diese Technik intern. Wenn Sie einen StringBuilder ToString übergeben, übergibt er Ihnen einfach seinen internen Puffer. StringBuilder.ToString ist oft O (1). – usr

2

, wenn Sie es wirklich schnell tun wollen, würde ich eine Zustandsmaschine

verwenden diese aussehen könnte:

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

Meinen Sie manuelle Parsing, ohne TryParse zu verwenden? – Vlad

+0

Ja, wenn Sie TryParse verwenden, benötigen Sie jedes Mal eine neue String-Instanz. dann haben Sie das gleiche Verhalten wie var values ​​= string.Split (''). Wählen Sie (s => double.Parse (s)). ToArray(); – user287107

+0

Nun, manuelles Parsen neigt dazu, langsam und fehlerhaft zu sein, ich möchte das Rad nach Möglichkeit nicht neu erfinden. – Vlad