Задание: найти максимальную длину текста между тэгами в байтах.
Предположим, у нас был исходный файл
<p> текст с выделенным <b> словом </b> </p>
characterDataHandler(void* userData, const XML_Char* s, int len)
, чтобы подсчитать количество символов между тэгами.
Функция characterDataHandler(void* userData, const XML_Char* s, int len)
вызывается не для текста между тэгами. Неизвестно, сколько раз вызовется эта функция между двумя последовательными тэгами. SAX хендлеры используют константную память, поэтому если между тэгами достаточно большой текст без переводов строк, то функция вызовется не один раз.
Минусы глобальных переменных:
- засоряем пространство имен
- невозможно использовать парсер для двух документов. Если хотим использовать для нескольких документов, то, после каждого использования, надо аккуратно обнулять документы.
- непонятно, что получится, если запустить парсер на двух документах параллельно.
Избавление от глобальных переменных с помощью статических не решает всех проблем, а решает только проблему с засорением пространства имен.
static size_t temp
Не надо "прятать" static переменные в функциях. Это не решает ни одной проблемы, а только уменьшает понятность кода. Становится сложнее отловить возможные проблемы со static переменнными. Т.е. не стоит писать:
void characterDataHandler(void *userData,const XML_Char *s,int len){ static size_t temp; ... }
class XMLCounterData{ private: size_t TagCounter; size_t AttributeCounter; size_t MaxTextLength; size_t CurrentTextLength; }; static void charDataHandler(void *userData,const XML_Char *s,int len){ XMLCounterData& counter = *(XMLCounterData*)userData; counter.MaxTextLength = ... }А в
main
пишем:
XML_Parser first = XML_ParserCreate(NULL); count a; XML_SetUserData(first,&a); XML_SetElementHandler(first, start, end); XML_SetCharacterDataHandler(first, charDataHandler);
Вариант решения задачи с помощью статической функции класса:
class XMLCounterData{ private: size_t TagCounter; size_t AttributeCounter; size_t MaxTextLength; size_t CurrentTextLength; public: static void charDataHandler(void *userData,const XML_Char *s,int len); }; void XMLCounterData::charDataHandler(void *userData,const XML_Char *s,int len){ XMLCounterData& counter = *(XMLCounterData*)userData; counter.MaxTextLength = ... }Чтобы создать парсер и установить
CharDataHandler
в main()
пишем:
XML_Parser first = XML_ParserCreate(NULL); count a; XML_SetUserData(first,&a); XML_SetElementHandler(first, start, end); XML_SetCharacterDataHandler(first, a.charDataHandler);Этот вариант лучше тем, что мы не засоряем пространство имен в нашем файле и становится понятнее к какой структуре относится хэндлер.
for(int i=1;i<2+3;++i)лучше писать так:
for (int i = 1; i < 2 + 3; ++i)
for ( i = 1; i < 2 + 3; ++i )
XML_Parser first = XML_ParserCreate(NULL); int a[4]; XML_SetUserData(first, &a);Лучше писать так:
XML_Parser first = XML_ParserCreate(NULL); count a; XML_SetUserData(first, &a);Лучше использовать структуру, вместо массива, так как поля структуры имеют говорящие названия, вследствие чего увеличивается понятность кода.
std::pairЛучше писать так:a (5,4);
struct point{ int x; int y; point ( int xx, int yy): x(a), y(b){}; } point a (5,4);Часто нужно хранить два числа. Не надо хранить их в
std::pair
, в которой поля названы first
и second
. Это снижает понятность кода. Заводите структуру.
void foo(){ ... return; }Не стоит писать непринятых вещей. Не стоит писать
return
в конце функции без возвращаемого значения.(std::string)"Hello";Лучше писать:
std::string("Hello");Не стоит приводить
char*
к string
с помощью конструктора копирования, а не приведения типов. Это работает одинаково, но второй способ понятнее.
std::pair<int,std::string>(1,s);Лучше писать:
std::make_pair(1,s)Используйте стандартные функции.
std::ifstream("fiel.txt",ifstream::in)Лучше писать:
std::ifstream("fiel.txt")Не стоит передавать в функции параметры, которые задаются по умолчанию и усложняют читабельность кода.
private
,public
,protected
в объявлении класса.
Не бойтесь писать несколько private подряд.
friend foo()
не является объявлением функции foo()
. Пример:
class Complex{ private: int myRe; int myIm; public: Complex(int re = 0, int im = 0); friend std::ostream& operator<<(std::ostream& out, const Complex a); // не является объявлением }; std::ostream& operator<<(std::ostream& out, const Complex a); // объявление
if (myRe != 0){ if (myIm > 0) os<< myRe<< "+"<< myIm; if (myIm == 0) os<< myRe; if ( myIm < 0) os<< myRe<< myIm; } if (myRe == 0){ ... }Лучше писать:
if (myRe != 0){ os<< myRe; if ( myIm > 0) os<<"+"; }
StartHandler
и EndHandler
встречаются одинаковый кусок кода:
if ( counter.CurrentTextLength > counter.MaxTextLength ){ counter.MaxTextLength = counter.CurrentTextLength; } counter.CurrentTextLength = 0;Поэтому его надо вынести в отдельную функцию. В структуре пишем новый метод:
class XMLCounterData{ private: size_t TagCounter; size_t AttributeCounter; size_t MaxTextLength; size_t CurrentTextLength; public: static void charDataHandler(void *userData,const XML_Char *s,int len); private: void maxLength(){MaxTextLength = std::max( MaxTextLength, CurrentTextLength);}; };
char* buffer = new char[1024]; XML_Parser parser = XML_Parser_Create(); FILE* file = fopen("..."); for( ){ if (/*произошла ошибка*/ ) fclose(file); XML_Parser_Close(parser); delete[] buffer; return 1; } fclose(file); XML_Parser_Close(parser); delete[] buffer; return 0; }Если выделить в отдельную функцию повторяющийся код, то получится, что передача параметров будет почти такая же длинная, как и сама функция. Поэтому мы выделим в отдельную функцию содержательный код:
int doParse(char* buffer,XML_Parser& parser, FILE* file){ for( ){ if (/*произошла ошибка*/ ) return 1; } return 0; } }А в функцию
main()
запишем
char* buffer = new char[1024]; XML_Parser parser = XML_Parser_Create(); FILE* file = fopen("..."); int code = doParse( buffer, parser, file); fclose(file); XML_Parser_Close(parser); delete[] buffer; return code;Кроме того, что мы избавились от повторяющегося кода, мы ещё разделили смысловую часть со вспомогательной.
foo.h
есть описание функции printf(int)
и мы включили сначала stdio.h
, а потом наш заголовочный файл. Тогда нам компилятор выдаст ошибку, что в каком-то системном файле, в какой-то строчке есть ошибка. Довольно проблематично найти строку, в которой произошла ошибка. А если мы включим заголовочные файлы в обратном порядке, то ошибка будет найдена в нашем заголовочном файле, соответственно будет легче разобраться в причинах ошибки.
using namespace std
. Любой системный заголовочный файл может включать другие заголовочные файлы, а в разных версиях библиотек могут включаться разные заголовочные файлы в данный header. В связи с чем, может на одном компьютере может не быть ошибки коллизии обозначений, а на другом, она может появиться.
Пример:
using namespace std; struct count{ int tag_count; int attr_count; int max_len; int temp; count(){tag_count =0; attr_count = 0; max_len = 0; temp = 0;}; }; static void start(void *data, const char *el, const char **attr){ count* c =(count*) data; (*c).tag_count += 1 ; for (int i = 0; attr[i]; i += 2) { (*c).attr_count += 1; } }В функции
start
выдавалась ошибка, которая отсылала в какие-то системные файлы. Оказалось, что уже существует функция count()
.