2008-10-23 16 views
5

Estoy tratando de cambiar la entrada del usuario en forma de comodín ("*word*") a un formato de expresión regular. A tal fin, estoy usando el siguiente código para quitarse el '*' al principio y al final de la entrada para que yo pueda añadir los caracteres de expresiones regulares en cada extremo:std :: string ¿se borra el último carácter?

string::iterator iter_begin = expressionBuilder.begin(); 
string::iterator iter_end = expressionBuilder.end(); 
iter_end--; 
if ((char)*iter_begin == '*' && (char)*iter_end == '*') 
{ 
    expressionBuilder.erase(iter_begin); 
    expressionBuilder.erase(iter_end); 
    expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b"; 
} 

Sin embargo, la llamada a "expressionBuilder.erase(iter_end)" ¿no borran el '*' de la cadena de entrada por lo que termino con una expresión regular incorrecta. ¿Qué estoy haciendo mal aquí? "(char)*iter_end == '*'" debe ser verdadero para el código dentro de la sentencia if para ejecutarse (lo que hace), entonces ¿por qué no funciona el mismo iterador cuando se pasa a borrar()?

Respuesta

3

Pruebe de borrar en el orden contrario:

expressionBuilder.erase(iter_end); 
expressionBuilder.erase(iter_begin); 

Después de borrar la primera *, iter_end referencia a un carácter más allá del final de la cadena en su ejemplo. El STL documentation indica que los iteradores están invalidados por erase(), por lo que técnicamente mi ejemplo también es incorrecto, pero creo que funcionará en la práctica.

+0

suerte con las cadenas que no es necesario usar iteradores, la mayoría de las funciones tienen una forma que tiene un índice en su lugar. Aún así, como dices, incluso con el borrado indexado debería hacerse "de atrás hacia adelante". –

+0

P4tXrx5jrMlbhyludk9pxHBT30kGHo9n: tienes razón sobre end(), pero hay un iter_end-- ahí que mira el último carácter real de la cadena. –

+0

Esto tiene mucho sentido, y revertir el orden solucionó el problema. ¡Gracias! – jeffm

1

(Revisado, ya que olvidé la línea iter_end--).

Probablemente desee una instrucción if que solo compruebe si *iter_begin == '*', y luego llama al find() para obtener la otra '*'. O puede usar rbegin() para obtener el "iterador inicial de la secuencia en reversa", avanzarlo uno y luego llamar al base() para convertirlo en un iterador regular. Eso te dará el último personaje de la secuencia.


Aún mejor, std::string tiene rfind() and find_last_of() methods. Le darán el último '*'. También puede llamar simplemente replace() en lugar de despojar a los '*' s y luego agregar el nuevo material de vuelta en

+0

Observe que hay un iter_end-- que respalda un caracter. –

+0

¿Echas de menos el "iter_end--;" línea, que mueve el iterador de nuevo al último elemento? Estoy seguro de que la respuesta de Greg es correcta, porque los iteradores de cadena son básicamente solo índices, por lo que el índice final se invalida por el primer borrado. – Roddy

+0

Estaba tratando de evitar "find_last_of" porque ya sé dónde está el personaje, pero tal vez estaba pensando demasiado. – jeffm

7

Su código original y las soluciones propuestas hasta ahora tienen un par de problemas, además del problema evidente que has publicado.:

  • uso de iteradores invalidada después de que la cadena se modifica
  • dereferencing iteradores posiblemente no válidas incluso antes de que se modifica la cadena (si la cadena está vacía, por ejemplo)
  • un error si la cadena ExpressionBuilder contiene sólo una canta Le '*' carácter

Ahora, los dos últimos elementos no podría ser realmente un problema si el código que utiliza el fragmento/rutina ya está validando que la cadena tiene al menos 2 caracteres, pero en caso de que no es el situación, creo que el siguiente para ser más robusto en la cara de valores arbitrarios para ExpressionBuilder:

// using the reverse iterator rbegin() is a nice easy way 
//  to get the last character of a string 

if ((expressionBuilder.size() >= 2) && 
    (*expressionBuilder.begin() == '*') && 
    (*expressionBuilder.rbegin() == '*')) { 

    expressionBuilder.erase(expressionBuilder.begin()); 

    // can't nicely use rbegin() here because erase() wont take a reverse 
    // iterator, and converting reverse iterators to regular iterators 
    // results in rather ugly, non-intuitive code 
    expressionBuilder.erase(expressionBuilder.end() - 1); // note - not invalid since we're getting it anew 

    expressionBuilder = "\\b\\w*" + expressionBuilder + "\\w*\\b"; 
} 

Tenga en cuenta que este código no funcionará si no expressionBuilder es "", "*", o "**" en que no lleva a cabo las acciones definidas . Sin embargo, es posible que no produzca los resultados que desea en esos casos (porque no sé exactamente qué es lo que quiere en esos casos). Modifique para adaptarse a sus necesidades.

+0

Gracias. A estas alturas, prácticamente sé que la cadena no está vacía o "*", pero estoy de acuerdo en que sería mejor codificarla de esa manera en caso de que algo cambie más adelante. – jeffm

+0

muy bien - solo usé esto en algunos de mis códigos también – danio

0

Menos el manejo de errores, que podría probablemente hacerlo de esta manera:

#include <iostream> 
#include <string> 
using namespace std; 

string stripStar(const string& s) { 
    return string(s.begin() + 1, s.end() - 1); 
} 

int main() { 
    cout << stripStar("*word*") << "\n"; 
} 
+0

¿Qué pasa si llamas 'stripStar (" palabra ")' o incluso 'stripStar (" palabra * ")'? Creo que OP quiere esta versatilidad. – Cosine

Cuestiones relacionadas