4.3.3. Borrar caracteres de cadenas

Borrar caracteres es fácil y eficiente con la función miembro erase(), que toma dos argumentos: donde empezar a borrar caracteres (que por defecto es 0), y cuantos caracteres borrar (que por defecto es string::npos). Si especifica más caracteres que los que quedan en el string, los caracteres restantes se borran igualmente (llamando erase() sin argumentos borra todos los caracteres del string). A veces es útil abrir un fichero HTML y borrar sus etiquetas y caracteres especiales de manera que tengamos algo aproximadamente igual al texto que obtendríamos en el navegador Web, sólo como un fichero de texto plano. El siguiente ejemplo usa erase() para hacer el trabajo:

//: C03:HTMLStripper.cpp {RunByHand}
//{L} ReplaceAll
// Filter to remove html tags and markers.
#include <cassert>
#include <cmath>
#include <cstddef>
#include <fstream>
#include <iostream>
#include <string>
#include "ReplaceAll.h"
#include "../require.h"
using namespace std;

string& stripHTMLTags(string& s) {
  static bool inTag = false;
  bool done = false;
  while(!done) {
    if(inTag) {
      // The previous line started an HTML tag
      // but didn't finish. Must search for '>'.
      size_t rightPos = s.find('>');
      if(rightPos != string::npos) {
        inTag = false;
        s.erase(0, rightPos + 1);
      }
      else {
        done = true;
        s.erase();
      }
    }
    else {
      // Look for start of tag:
      size_t leftPos = s.find('<');
      if(leftPos != string::npos) {
        // See if tag close is in this line:
        size_t rightPos = s.find('>');
        if(rightPos == string::npos) {
          inTag = done = true;
          s.erase(leftPos);
        }
        else
          s.erase(leftPos, rightPos - leftPos + 1);
      }
      else
        done = true;
    }
  }
  // Remove all special HTML characters
  replaceAll(s, "&lt;", "<");
  replaceAll(s, "&gt;", ">");
  replaceAll(s, "&amp;", "&");
  replaceAll(s, "&nbsp;", " ");
  // Etc...
  return s;
}

int main(int argc, char* argv[]) {
  requireArgs(argc, 1,
    "usage: HTMLStripper InputFile");
  ifstream in(argv[1]);
  assure(in, argv[1]);
  string s;
  while(getline(in, s))
    if(!stripHTMLTags(s).empty())
      cout << s << endl;
} ///:~

Listado 4.21. C03/HTMLStripper.cpp


Este ejemplo borrará incluso las etiquetas HTML que se extienden a lo largo de varias líneas.[5] Esto se cumple gracias a la bandera estática inTag, que evalúa a cierto si el principio de una etiqueta es encontrada, pero la etiqueta de finalización correspondiente no es encontrada en la misma línea. Todas la formas de erase() aparecen en la función stripHTMLFlags().[6] La versión de getline() que usamos aquí es una función (global) declarada en la cabecera de string y es útil porque guarda una línea arbitrariamente larga en su argumento string. No necesita preocuparse de las dimensiones de un arreglo cuando trabaja con istream::getline(). Nótese que este programa usa la función replaceAll() vista antes en este capítulo. En el póximo capitulo, usaremos los flujos de cadena para crear una solución más elegante.



[5] Para mantener la exposición simple, esta version no maneja etiquetas anidadas, como los comentarios.

[6] Es tentador usar aquí las matemáticas para evitar algunas llamadas a erase(), pero como en algunos casos uno de los operandos es string::npos (el entero sin signo más grande posible), ocurre un desbordamiento del entero y se cuelga el algoritmo.