2012-04-15 22 views
6

Este es un spin-off de la discusión en some other question.Parse sin string split

Supongamos que tengo que analizar una gran cantidad de cadenas muy largas. Cada cadena contiene una secuencia de double s (en representación de texto, por supuesto) separados por espacios en blanco. Necesito analizar el double s en un List<double>.

La técnica de análisis estándar (usando string.Split + double.TryParse) parece ser bastante lenta: para cada uno de los números debemos asignar una cadena.

Intenté hacer una vieja forma de C: calcule los índices del comienzo y el final de las subcadenas que contienen los números, y analícelos "en su lugar", sin crear cadenas adicionales. (Ver http://ideone.com/Op6h0, a continuación se muestra la parte correspondiente.)

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

Hay una sobrecarga de string.IndexOf, buscando sólo dentro de una subcadena dada, pero no pude encontrar un método para analizar un doblete de subcadena, sin tener que extraer que subcadena primero.

¿Alguien tiene una idea?

+5

¿ha demostrado que esto es en realidad un cuello de botella? No sé * de ninguna manera de hacerlo fuera de la mano, pero ciertamente quiero alguna evidencia de que sea un problema antes de la micro-optimización. –

+0

@Jon: en realidad no. La pregunta se basa en la discusión en la pregunta vinculada (http://stackoverflow.com/questions/10053449/extract-numbers-from-string). Lo siento por eso. – Vlad

+0

Bastante justo. Sospecho que una rutina de análisis escrita a mano sería más lenta que el método presumiblemente optimizado con mucha experiencia que el equipo de BCL ha ideado :) –

Respuesta

7

No hay sin API administrada para analizar un doble de una subcadena. Mi suposición es que la asignación de la cadena será insignificante en comparación con todas las operaciones de punto flotante en doble.

De todos modos, puede guardar la asignación creando una cadena de "búfer" una vez de longitud 100 que consta únicamente de espacios en blanco. Luego, para cada cadena que quiera analizar, copie los caracteres en esta cadena de buffer usando código de inseguridad. Usted llena la cadena de buffer con espacios en blanco. Y para el análisis puede usar NumberStyles.AllowTrailingWhite que causará que se ignoren los espacios en blanco finales.

Conseguir un puntero a la cadena es en realidad una operación totalmente compatible:

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

C# tiene una sintaxis especial para atar una cuerda a un char *.

+0

¿Lo entiendo correctamente: se refiere a modificar un 'System.String' supuestamente inmóvil con código inseguro? – Vlad

+0

¿No sería más lento analizar todos esos espacios en blanco que asignar una nueva cadena cada vez? – svick

+0

@Vlad, sí, puedes hacer eso. Simplemente no pases esa cuerda y la mantengas en privado. De esta forma, no violará las suposiciones que hace otro código. StringBuilder utiliza esta técnica internamente. Cuando toString un StringBuilder simplemente te entrega su búfer interno. StringBuilder.ToString a menudo es O (1). – usr

2

si quiere hacerlo muy rápido, me gustaría utilizar una máquina de estados

esto podría verse como:

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

¿Te refieres a un análisis manual sin usar TryParse? – Vlad

+0

sí, si está utilizando TryParse, necesita cada vez una nueva instancia de cadena. entonces usted tiene el mismo comportamiento como var values ​​= string.Split ('') .Select (s => double.Parse (s)). ToArray(); – user287107

+0

bueno, el análisis manual tiende a ser lento y con errores, me gustaría evitar reinventar la rueda si es posible. – Vlad