2009-07-22 21 views
5

Estoy trabajando en un código que normaliza una gran cantidad de datos. Al final del procesamiento, se escribe un número de pares clave = "valor" en un archivo. .La implementación de escapada más rápida?

La parte "valor" puede ser cualquier cosa, por lo que en el punto de salida de los valores deben tener ningún comillas incrustadas escaparon como \"

En este momento, estoy usando la siguiente:

outstream << boost::regex_replace(src, rxquotesearch, quoterepl); 
// (where rxquotesearch is boost::regex("\"") and quoterepl is "\\\\\"") 

Sin embargo, gprof muestra que estoy gastando la mayor parte de mi tiempo de ejecución en este método, ya que tengo que llamarlo para cada valor.

Tengo curiosidad si hay una manera más rápida que esta. no puedo usar std :: replace ya que estoy reemplazando un caracter por dos.

Gracias por cualquier consejo.

Respuesta

5

Si la velocidad es una preocupación que debe utilizar una función escrita a mano para hacer esto. Observe el uso de reserve() para intentar mantener la asignación de memoria (re) al mínimo.

string escape_quotes(const string &before) 
{ 
    string after; 
    after.reserve(before.length() + 4); 

    for (string::size_type i = 0; i < before.length(); ++i) { 
     switch (before[i]) { 
      case '"': 
      case '\\': 
       after += '\\'; 
       // Fall through. 

      default: 
       after += before[i]; 
     } 
    } 

    return after; 
} 
+0

¿debería reservar antes del.length + 10% más o menos? suponiendo que haya al menos una cita incrustada, tendremos que crecer? – DeusAduro

+0

Sí, fudge con eso según sea necesario. –

+0

Entonces, tengo curiosidad sobre el comentario que publiqué a continuación. ¿Alguna idea de por qué esto sería más rápido que la salida de cada personaje directamente a la secuencia dentro del ciclo for? – Joe

0

Bueno, aquí hay una implementación usando string :: find y string :: insert, no estoy seguro de si es más rápido, ¡tendrás que resolverlo! Aquí está:

std::string src = "hey there i have \" all \" over the f\"in pla\"ce\""; 
size_t n = 0; 
while ((n=src.find("\"",n)) != std::string::npos) 
{ 
    src.insert(n,"\\"); 
    n+=2; 
} 
std::cout << src << std::endl; 

Qué impreso:

Hola he \ "all \" sobre el f \ "en el Pla \" ce \"

1

I' No estoy sorprendido de que la expresión regular sea muy lenta aquí: estás usando un gran martillo de uso general para clavar un pequeño clavo. Por supuesto, si terminaras necesitando hacer algo más interesante, la expresión regular podría ganar rápidamente el ventaja en términos de simplicidad.

En cuanto a un enfoque más simple/más rápido, podría intentar escribir la cadena escapada en un búfer separado, un carácter a la vez. Entonces se vuelve trivial agregar los escapes, y no pierdes el tiempo reasignando la cadena o cambiando los caracteres. La mayor dificultad será administrar el tamaño de tu búfer, pero puedes usar un vector para eso y reutilizar el mismo vector para cada cuerda para evitar asignaciones repetidas. La ganancia de eficiencia dependería en gran medida de los detalles de cómo funciona el vector, pero siempre se puede reducir a matrices en bruto y administración de memoria manual, si es necesario.

La rutina podría ser algo como esto, si utilizó el vector:

vector<char> buf; 
for(some_iterator it = all_the_strings.begin(); 
    it != all_the_strings.end(); ++it) 
{ 
    buf.clear(); 
    const string & str = *it; 
    for(size_t i = 0; i < str.size(); ++i) 
    { 
     if(str[i] == '"' || str[i] == '\\') 
      buf.push_back('\\'); 
     buf.push_back(str[i]); 
    } 
    buf.push_back('\0'); 

    // note: this is not guaranteed to be safe, see answer comments 
    const char * escaped = &buf[0]; 

    // print escaped string to file here... 
} 
+0

Con optimizaciones y preasignación, el vector debe ser igual de rápido la mayoría de las veces (las veces que no se requiere crecer). La suya es probablemente más rápida que la mía, dado el cambio que está ocurriendo en la mía. – DeusAduro

+1

Eek! '& buf [0]' no es la mejor idea ... –

+0

Puedo creer que probablemente no sea el mejor, ¿pueden explicar por qué y/o sugerir una mejor alternativa? – Charlie

2

No tomaría la cadena fuente y construiría una nueva cadena de salida.
Me gustaría iterar a través de la cadena fuente e imprimir cada carácter, si el carácter es una cita, simplemente imprima una "\" antes de imprimirla.

+0

Curiosamente, tomé el fragmento de John Kugelman y lo usé para escribir directamente en la transmisión, y es mucho más lento que construir primero la secuencia temporal y escribir toda la cadena en la secuencia. ¡No es lo que esperaba en absoluto! – Joe

+0

@Joe: Tiene sentido para mí, porque requiere N llamadas a la función de salida de flujo, que probablemente tiene un poco de lógica dentro. Si construye la salida en un búfer temporal, puede (si es necesario) bajarlo a una pequeña cantidad constante de llamadas al método y un bucle interno bastante ajustado, que parece mucho más eficiente. Si la velocidad es lo que realmente importa aquí, no será mucho más rápido que eso. – Charlie

+0

Solo sirve para demostrar que no sabe hasta que realmente codifica y lo intenta. Este "podría" ser más rápido en una máquina o compilador diferente, todo depende de la sobrecarga de impresión de caracteres individuales versus cadenas completas. – KPexEA

Cuestiones relacionadas