2008-10-23 20 views
5

J'essaye de changer l'entrée d'utilisateur dans la forme générique ("*word*") dans un format d'expression régulière. À cette fin, je suis en utilisant le code ci-dessous pour dépouiller l''*' au début et à la fin de l'entrée afin que je puisse ajouter les caractères d'expression régulière à chaque extrémité:std :: string effacer le dernier caractère échoue?

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"; 
} 

Cependant, l'appel à "expressionBuilder.erase(iter_end)"pas effacer le '*' à la fin de la chaîne d'entrée de sorte que je termine avec une expression régulière incorrecte. Qu'est-ce que je fais mal ici? "(char)*iter_end == '*'" doit être vrai pour le code dans l'instruction if à exécuter (ce qu'il fait), alors pourquoi le même itérateur ne fonctionne-t-il pas lorsqu'il est passé à erase()?

Répondre

3

Essayez de les effacer dans l'ordre inverse:

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

Après avoir effacé la première *, iter_end fait référence à un caractère après la fin de la chaîne dans votre exemple. Le STL documentation indique que les itérateurs sont invalidés par erase(), donc techniquement mon exemple est faux aussi mais je crois que cela fonctionnera dans la pratique.

+0

Heureusement, avec les chaînes que vous n'avez pas besoin d'utiliser les itérateurs, la plupart des fonctions ont une forme qui prend un index à la place. Pourtant, comme vous le dites, même avec un effacement indexé, il devrait toujours être fait "de l'avant". –

+0

P4tXrx5jrMlbhyludk9pxHBT30kGHo9n: vous avez raison à propos de end(), mais il y a un iter_end-- là-dedans qui regarde le dernier caractère de la chaîne. –

+0

Cela prend tout son sens, et inverser l'ordre a résolu le problème. Merci! – jeffm

1

(Révisé, car j'ai raté la ligne iter_end--).

Vous souhaitez probablement une instruction if qui vérifie uniquement si *iter_begin == '*', puis appelle find() pour obtenir l'autre '*'. Ou vous pouvez utiliser rbegin() pour obtenir le "début de l'itérateur de la séquence à l'envers", avancez-en un, puis appelez base() pour le transformer en un itérateur régulier. Cela vous obtiendra le dernier caractère de la séquence.


Mieux encore, std::string a rfind() and find_last_of() methods. Ils vous obtiendront le dernier '*'. Vous pouvez aussi simplement appeler replace() au lieu d'enlever les '*' s, puis en ajoutant les nouveautés de retour dans

+0

Notez qu'il y a un iter_end-- là-dedans qui sauvegarde un caractère. –

+0

Avez-vous manqué le "iter_end--;" ligne, qui déplace l'itérateur vers le dernier élément? Je suis sûr que la réponse de Greg est correcte, parce que les itérateurs de chaînes sont essentiellement des index, donc l'index de fin est invalidé par le premier effacement. – Roddy

+0

J'essayais d'éviter "find_last_of" parce que je sais déjà où se trouve le personnage, mais peut-être que je le pensais trop. – jeffm

7

Votre code d'origine et les solutions proposées ont à ce jour quelques problèmes en plus du problème évident que vous avez posté au sujet.:

  • utilisation de itérateurs invalidée après la chaîne est modifiée
  • déréférencement itérateurs peut-être invalides avant même la chaîne est modifiée (si la chaîne est vide, par exemple)
  • un bug si la chaîne de expressionBuilder contient seulement chanter Le caractère '' '

Maintenant, les deux derniers éléments ne peuvent pas vraiment être un problème si le code qui utilise le snippet/routine valide déjà que la chaîne a au moins 2 caractères, mais dans le cas où ce n'est pas le situation, je crois que ce qui suit pour être plus robuste face à des valeurs arbitraires pour 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"; 
} 

Notez que ce code fonctionne lorsque expressionBuilder est "", "*" ou "**" en ce qu'elle ne réalise aucune action non défini . Cependant, il se peut que cela ne produise pas les résultats que vous voulez dans ces cas (c'est parce que je ne sais pas exactement ce que vous voulez dans ces cas-là). Modifiez en fonction de vos besoins.

+0

Merci. Je sais à peu près à ce stade que la chaîne n'est pas vide ou "*", mais je suis d'accord qu'il serait préférable de le coder de cette façon juste au cas où quelque chose change plus tard. – jeffm

+0

très agréable - juste utilisé dans certains de mon code aussi – danio

0

Moins la gestion des erreurs, vous pouvez probablement le faire comme ceci:

#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

Que faire si vous appelez 'stripStar (" mot ")' ou même 'stripStar (" mot * ")'? Je pense que OP veut cette polyvalence. – Cosine

Questions connexes