Вернуться на Страничку новостей.

02.01.2009

Программа - Фильтр для удаления тегов и маркеров HTML

Известно, что для того, чтобы текстовый файл правильно отображался в браузере, необходимо в нем расставить изрядно количестов тегов. (как правило десятки и сотни). Однажды я написал утилиту, которая расставляет теги (так, как мне надо, а не так как в Word). Затем написал утилиту, которая "обезвреживает" теги, то есть делает их видимыми в браузере. Эти программы служат мне верой и правдой уже долгое время.

Но назрела необходимость иметь утилиту для удаления тегов из html-файла. И тут я счасливо нахожу эту утилиту в книге Брюса Эккеля "Thinking in C++". Написана она мастерски! Ничего лишнего.

Ниже представлен вариант программы удаления тегов из html-файла из 3-главы Брюса Эккеля.
По сравнению с предыдущей версией, эта программа считывает исходный файл inFile с тегами,
и переписывает его в результирующий файл outFile без тегов.

Для этого пришлось изменить лишь несколько строк в главной функции main()

Измененная функция main()


int main(int argc, char* argv[]) {

ifstream in("inFile", ios::binary);
if (!in) {
cout << "Unable to open " << "inFile" << " for reading.\n";
return(1);
}

ofstream out("outFile",ios::binary);
if (!out) {

cout << "Unable to open " << "outFile" << " for writing.\n";
return(1);
}

string s;
while(getline(in, s))
if (!stripHTMLTags(s).empty())
out << s << endl;

} ///:~

Ниже показана вся модифицированная программа.

Вверх

Фильтр для удаления тегов и маркеров HTML (новый вариант)


//: C03:HTMLStripper.cpp {RunByHand}
//{L} ReplaceAll
// Фильтр для удаления тегов и маркеров HTML
#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) {
// В предыдущей строке начался, но не закончился тег HTML.
// Продолжаем поиск '>'.
size_t rightPos = s.find('>');
if (rightPos != string::npos) {
inTag = false;
s.erase(0, rightPos + 1);
}
else {
done = true;
s.erase();
}
}
else {
// Поиск начала тега:
size_t leftPos = s.find('<');
if (leftPos != string::npos) {
// Проверить, закрывается ли тег в текущей строке
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;
}
}
// Удаление всех специальных символов HTML
replaceAll(s, "& lt;", "& lt");
replaceAll(s, "& gt;", "& gt");
replaceAll(s, "& amp;", "&");
replaceAll(s, "& nbsp;", " ");
// E o. a.
return s;
}

int main(int argc, char* argv[]) {


ifstream in("inFile", ios::binary);
if (!in) {
cout << "Unable to open " << "inFile" << " for reading.\n";
return(1);
}

ofstream out("outFile",ios::binary);
if (!out) {

cout << "Unable to open " << "outFile" << " for writing.\n";
return(1);
}

string s;
while(getline(in, s))
if (!stripHTMLTags(s).empty())
out << s << endl;

} ///:~


Анализ:

Программа удаляет даже теги html, занимающие несколько строк. Для этого используется статический флаг inTag, которому присваивается true, если при обнаружении начального тега парный завершающий тег не был обнаружен в той же строке. В функции stripHTMLTags() встречаются все формы функции erase().

Используемая версия getline() представляет собой глобальную функцию, объявленную в заголовочном файле <string> она удобна тем, что в аргументе string может храниться строка произвольной длины. Нам не приходится беспокоиться о размерах символьного массива, как при использовании функции stream::getline().

В программе задействована функция replaceAll(), упоминавшаяся ранее в этой главе. В следующей главе будет создано более элегантное решение с применением строковых потоков. Внутри программы удаления тегов есть функция replaceAll(), которая заменяет любую подстроку в строке на новую подстроку (конкретно в этой программе она использована для замены маркеров). Функция replaceAll() оформлена в виде двух отдельных файлов - заголовочного h-файла и cpp-файла. Нахожу эту функцию весьма полезной!

Вверх

Функция замены всех вхождений одной подстроки - другой подстрокой:


//: C03:ReplaceAll.h
#ifndef REPLACEALL_H
#define REPLACEALL_H
#include <string>

std::string& replaceAll(string& context,
const string& from, const string& to);
#endif // REPLACEALL_H ///:~

//: C03:ReplaceAll.cpp {O}
#include <cstddef>
#include "ReplaceAll.h"
using namespace std;

string& replaceAll(string& context, const string& from, const string& to) {

size_t lookHere = 0;
size_t foundHere;
while ((foundHere = context.find(from, lookHere)) != string::npos) {
context.replace(foundHere, from.size(), to);
lookHere = foundHere + to.size();
}
return context;
} ///:~


Анализ:

Версия find(), использованная в этой программе, получает во втором аргументе начальную позицию поиска, и возвращает string::npos. Позицию, хранящуюся в переменной lookHere, важно сместить за строку замены на тот случай, если from является подстрокой to.

Hosted by uCoz