Разработка программы, динамически подсвечивающей ключевые слова из загружаемого пользователем словаря
Ошибка возникает потому, что сообщение EM_LINELENGTH использует только младшие 16 разрядов параметра. Этот параметр должен содержать номер любого символа из строки длину, которой надо определить. Если передается номер более 65 535, ну, например 65 536, то сообщение возвращает длину строки, содержащую символ с номером 0. Таким образом, в методе Add при запросе функции GetCount может быть получено… Читать ещё >
Разработка программы, динамически подсвечивающей ключевые слова из загружаемого пользователем словаря (реферат, курсовая, диплом, контрольная)
Задание на курсовую работу представляет собой программу, в которой нужно: реализовать структуры данных, необходимые для выполнения задания курсового проекта, а также реализация алгоритмов, например, поиска. Структура данных и алгоритмы выбираются самостоятельно, в зависимости от подхода к решению данной курсовой задачи.
Основанием для разработки программы является задание к курсовому проекту по предмету «Структуры и алгоритмы компьютерной обработки данных». Получившаяся программа, должна быть максимальна, направлена на пользователя и обладать такими признаками, как легкость в использовании, удобный интерфейс, быстроту работы.
Требуется создать программу, динамически подсвечивающую ключевые слова из загружаемого пользователем словаря.
Выполним постановку задачи и приведем условия, которым должны удовлетворять входные данные, а также требования к необходимому результату: 1) пользователь может открыть текстовый файл и подключить словарь, затем начать работу в программе; 2) пользователь может сразу подключить словарь и начать работу в программе.
В соответствии с заданием, мною была разработана программа, имеющая простой для пользователя интерфейс, реализующая динамическую подсветку ключевых слов, причем пользователь может открыть для использования произвольный текстовый файл любого расширения. Редактирование словаря производится в любом текстовом редакторе.
Для подсвечивания ключевых слов необходимо решить следующие задачи:
1) чтение ключевых слов из файла словаря;
2) открытие, редактирование и сохранение файлов;
3) поиск и выделение ключевых слов.
Теоретический материал
В программе используются дополнительные данные — такие как текст. Эти дополнительные данные неудобно хранить в исходном коде программы — они хранятся в отдельных файлах. Именно для получения доступа к этим данным и используются возможности ввода/вывода языка C++.
В C++ есть два способа получить доступ к файлам: потоки (streams) и доступ к файлам, унаследованный от C. В программе будут использоваться только потоки. Рассмотрим один из важных операторов — sizeof.
Оператор sizeof возвращает размер в байтах объекта или типа данных. Синтаксис его таков:
sizeof (type name);
sizeof (object);
sizeof object;
Результат имеет специальный тип size_t, который определен как typedef в заголовочном файле cstddef. Вот пример использования обеих форм оператора sizeof:
#include
int ia[] = {0, 1, 2};
// sizeof возвращает размер всего массива
size_t array_size = sizeof ia;
// sizeof возвращает размер типа int
size_t element_size = array_size / sizeof (int);
Применение sizeof к массиву дает количество байтов, занимаемых массивом, а не количество его элементов и не размер в байтах каждого из них. Так, например, в системах, где int хранится в 4 байтах, значением array_size будет 12. Применение sizeof к указателю дает размер самого указателя, а не объекта, на который он указывает:
int *pi = new int[3];
size_t pointer_size = sizeof (pi);
Здесь значением pointer_size будет память под указатель в байтах (4 в 32-битных системах), а не массива ia. Вот пример программы, использующей оператор sizeof:
#include
#include
#include
int main () {
size_t ia;
ia = sizeof (ia); // правильно
ia = sizeof ia; // правильно
// ia = sizeof int; // ошибка
ia = sizeof (int); // правильно
int *pi = new int[12];
cout << «pi: «<< sizeof (pi)
<< «*pi: «<< sizeof (pi)
<< endl;
// sizeof строки не зависит от
// ее реальной длины
string stl («foobar»);
string st2(«a mighty oak»);
string *ps = &stl;
cout << «st1: «<< sizeof (st1)
<< «st2: «<< sizeof (st2)
<< «ps: sizeof (ps)
<< «*ps: «<< sizeof (*ps)
<< endl;
cout << «short :t» << sizeof (short) << endl;
cout << «shorf» :t" << sizeof (short*) << endl;
cout << «short& :t» << sizeof (short&) << endl;
cout << «short[3] :t» << sizeof (short[3]) << endl;
}
Результатом работы программы будет:
pi: 4 *pi: 4
st1: 12 st2: 12 ps: 4 *ps:12
short: 2
short*: 4
short&: 2
short[3]: 6
Из данного примера видно, что применение sizeof к указателю позволяет узнать размер памяти, необходимой для хранения адреса. Если же аргументом sizeof является ссылка, мы получим размер связанного с ней объекта. Гарантируется, что в любой реализации С++ размер типа char равен 1.
// char_size == 1
size_t char_size = sizeof (char);
Значение оператора sizeof вычисляется во время компиляции и считается константой. Оно может быть использовано везде, где требуется константное значение, в том числе в качестве размера встроенного массива. Например:
// правильно: константное выражение
int array[sizeof (some_type_T)];
TStringList чрезвычайно полезен для многих видов обработок списков. Элементы в строковом списке могут быть вставлены, перемещены и отсортированы.
Список может быть сформирован строка за строкой, или загружен из большой строки разделенной запятой или даже из текстового файла. TStringList происходит от TStrings. Допускается использование и TStrings, но это не рекомендуется, так как он не полный — некоторые из его методов абстрактны. TStringList осуществляет эти абстрактные методы (Clear, Delete и Insert). Мы рассмотрим основные свойства и методы TStringList, включая полученные из TStrings.
Свойство Count возвращает число строк в списке. Свойство Capacity устанавливает или получает текущую вместимость строкового списка. Вы можете управлять этой вместимостью по необходимости.
Свойство Strings получает или корректирует строку по данному индексу в списке (первый элемент списка имеет индекс 0). Свойство Strings является свойством, заданным по умолчанию. Это означает, что вы можете использовать его без его указания:
myName := names. Strings[4];
является эквивалентным:
myName := names[4];
Свойство Text устанавливает или получает список в виде большой строки. Эта строка будет содержать каждую строку заканчивающуюся комбинацией символов перевода каретки и перевода строки (CRLF). Полезно для загрузки из визуального объекта, который может содержать многочисленные строки текста.
Свойство CommaText получает или устанавливает список в виде большой строки. Эта строка будет иметь список строк разделенных запятыми. Это полезно для загрузки из экспорта текстовой электронной таблицы. Если при получении строка содержит вложенные пространства, то она будет заключена в двойные кавычки.
Cвойство DelimitedText получает или устанавливает список через большую строку. Эта строка содержит список строк разделенных значением Delimiter (по умолчанию — запятая). Строки, содержащие вложенные пробелы должны быть заключены в QuoteChar (по умолчанию — «).
Свойство QuoteChar используется для замыкания строк, которые имеют вложенные пробелы при использовании DelimitedText. Свойство Delimiter используется для разделения строк при использовании DelimitedText.
Свойство Names. Строки в строковом списке могут быть обработаны, как пары название/значение, как во втором примере кода. Каждая строка не должна иметь никаких внедренных пробелов, и содержать знак =.
Свойство Values возвращает значение для данного названия, когда используются строки пары название/значение (см. выше). Свойство ValueFromIndex возвращает значение по индексу строки (начинается с 0), когда используются пары название/значение.
Свойство CaseSensitive. Когда true, Delphi обрабатывает строки чувствительно к регистру при выполнении некоторых операций, таких как Sort.
Свойство Duplicates может иметь одно из следующих перечислимых TDuplicates значений:
dupIgnore Игнорирует (отбрасывает) дубликаты
dupAccept Позволяют дубликаты
dupError Выбрасывает исключение, если имеются дубликаты Свойство Sorted. Когда true, все строки будут добавляться в свою позицию отсортированной последовательности. Когда false, они будут добавляться в конец. Свойство Objects возвращает объект, связанный со строкой по данному индексу, если он существует.
Метод Add добавляет данную строку в список, возвращая ее позицию в списке (начинается с 0). Метод Append равнозначен Add, но без возвращения индексного значения. Метод Insert вставляет строку в заданную индексом позицию. Позиция 0 вызовет вставку в начало. Метод Delete удаляет строку по данному индексу.
Метод Clear удаляет все строки из списка. Метод Move перемещает строку из одной позиции в другую, сдвигая другие строки соответственно.
Метод Exchange перестанавливает две строки в списке, идентифицированные по их индексным позициям. Метод IndexOf получает индекс позиции строки в списке соответствующей данной строке. Если строка не найдена, то возвращается -1.
Метод LoadFromFile загружает строковый список из текстового файла. Каждая текстовая строка (законченная CRLF — см. DelimitedText) становится строкой списка. Метод SaveToFile сохраняет строковый список в текстовый файл.
SelStart, SelLength
В библиотеке компонентов Delphi по поводу этих методов имеются следующие описания:
В файле SourceRTLWINrichedit. pas описание типа
type
TCharRange = record
cpMin: Longint;
cpMax: LongInt;
end;
Этот тип нужен для передачи границ выделенной части текста. Для Windows выделяемая часть текста указывается так, cpMin — первый символ, с которого начинается выделение (т.е. этот символ входит в выделение), а cpMax — первый символ не выделенного текста, сразу за выделением (т.е. этот символ не входит в выделение).
В файле SourceVCLstdctrls. pas
TCustomEdit = class (TWinControl)
public
property SelLength: Integer read GetSelLength write SetSelLength;
property SelStart: Integer read GetSelStart write SetSelStart;
Здесь определяются свойства SelStart и SelLength.
В файле SourceVCLcomctrls. pas
function TCustomRichEdit. GetSelStart: Integer;
var
CharRange: TCharRange;
begin
SendMessage (Handle, EM_EXGETSEL, 0, Longint (@CharRange));
Result := CharRange. cpMin;
end;
procedure TCustomRichEdit. SetSelStart (Value: Integer);
var
CharRange: TCharRange;
begin
CharRange.cpMin := Value;
CharRange.cpMax := Value;
SendMessage (Handle, EM_EXSETSEL, 0, Longint (@CharRange));
end;
При установке SelStart начало и конец, выделяемой части, приводятся к единому значению. При этом курсор переместится к указанному символу и компонент перерисует изображение, так, чтобы было видно курсор.
function TCustomRichEdit. GetSelLength: Integer;
var
CharRange: TCharRange;
begin
SendMessage (Handle, EM_EXGETSEL, 0, Longint (@CharRange));
Result := CharRange. cpMax — CharRange. cpMin;
end;
procedure TCustomRichEdit. SetSelLength (Value: Integer);
var
CharRange: TCharRange;
begin
SendMessage (Handle, EM_EXGETSEL, 0, Longint (@CharRange));
CharRange.cpMax := CharRange. cpMin + Value;
SendMessage (Handle, EM_EXSETSEL, 0, Longint (@CharRange));
SendMessage (Handle, EM_SCROLLCARET, 0, 0);
end;
При установке SelLength приходится читать текущее положение курсора (первое сообщение — EM_EXGETSEL), изменять значение конца выделения (cpMax := cpMin + Value) и снова устанавливать курсор туда же (второе сообщение — EM_EXSETSEL), но с выделением текста. При этом компонент опять перерисует изображение, что бы стало видно выделение. И, наконец, третье сообщение EM_SCROLLCARET заставляет компонент произвести прокрутку изображения так, чтобы курсор стал виден.
Метод FindText ищет строку-образец в указанном диапазоне текста.
Метод возвращает номер позиции искомого образца в тексте (от начала текста, с 0), если образец не найден, то возвращает -1 (минус один).
Из просмотра исходных текстов Unit из библиотеки компонентов Delphi следует, что TRichEdit наследует метод FindText от своего ближайшего предка TCustomRichEdit.
Из файла RTLWINrichedit. pas описание типа
type
TCharRange = record
cpMin: Longint;
cpMax: LongInt;
end;
Этот тип нужен для передачи границ той части текста, внутри которой будет производиться поиск. Для Windows границы текста указывается так, cpMin — первый символ, с которого начинается поиск, аcpMax — последний символ, до которого производится поиск.
TFindText = TFindTextA;
TFindTextA = record
chrg: TCharRange;
lpstrText: PAnsiChar;
end;
Поле lpstrText это указатель на null-terminated строку (PAnsiChar обычная PChar).
Из файла VCLcomctrls. pas
TSearchType = (stWholeWord, stMatchCase);
TSearchTypes = set of TSearchType;
Данный тип определяет, каким образом должен происходить поиск. Если установлен stMatchCase, то при поиске необходимо учитывать регистр букв (большие или маленькие). Если установлен stWholeWord, то необходимо, чтобы найденная последовательность была ограничена разделителями (например, пробелами). В OSR2rus это называется «только слово целиком», хотя слов может быть несколько.
TCustomRichEdit = class (TCustomMemo)
function FindText (const SearchStr: string; StartPos, Length: Integer; Options: TSearchTypes): Integer;
Это описательная часть класса и далее в разделе реализации:
function TCustomRichEdit. FindText (const SearchStr: string; StartPos, Length: Integer; Options: TSearchTypes): Integer;
var
Find: TFindText;
Flags: Integer;
begin
with Find. chrg do
begin
cpMin := StartPos;
cpMax := cpMin + Length;
end;
Flags := 0;
if stWholeWord in Options then Flags := Flags or FT_WHOLEWORD;
if stMatchCase in Options then Flags := Flags or FT_MATCHCASE;
Find.lpstrText := PChar (SearchStr);
Result := SendMessage (Handle, EM_FINDTEXT, Flags, LongInt (@Find));
end;
Сначала устанавливаются границы диапазона поиска, причем вместо длины используется номер последнего символа для поиска (cpMax := cpMin + Length). Затем устанавливается флаг поиска. Далее устанавливается ссылка на Pchar строку-образец. И, наконец, посылается сообщение EM_FINDTEXT самому себе.
Lines.Add добавляет строку в конец текста Класс TRichEdit наследует метод Add от класса TStrings, т.к. именно такой тип имеет свойство Lines. Из файла SourceVCLclasses. pas:
function TStrings. Add (const S: string) :Integer;
begin
Result := GetCount;
Insert (Result, S);
end;
Запрашиваем количество строк в тексте и вызываем метод Insert для вставки строки в конец текста. Обе используемые функции описаны там же, как:
TStrings = class (TPersistent)
protected
function GetCount: Integer; virtual; abstract;
public
procedure Insert (Index:Integer; const S: string); virtual; abstract;
То есть, абстрактны и виртуальны, а значит, в обязательном порядке должны быть переопределены в потомках. Таким потомком является класс TRichEditStrings, конструктор которого и вызывается для создания свойства Lines в конструкторе TCustomRichEdit, ближайшем родителе TRichEdit.
Из файла SourceVCLcomctrls. pas
TRichEditStrings = class (TStrings)
protected
function GetCount: Integer; override;
public
procedure Insert (Index: Integer; const S: string); override;
function TRichEditStrings. GetCount: Integer;
begin
Result := SendMessage (RichEdit.Handle, EM_GETLINECOUNT, 0, 0);
if SendMessage (RichEdit.Handle, EM_LINELENGTH, SendMessage (RichEdit.Handle, EM_LINEINDEX, Result — 1, 0), 0) = 0 then Dec (Result);
end;
Запрашиваем количество строк в тексте и сохраняем его в служебной переменной Result. Если длина последней строки равна нулю (т.е. пуста) вычитаем ее из результата (Result).
procedure TRichEditStrings. Insert (Index:Integer; const S: string);
var
L: Integer;
Selection: TCharRange;
Fmt: PChar;
Str: string;
begin
if Index >= 0 then
begin
Selection.cpMin := SendMessage (RichEdit.Handle, EM_LINEINDEX, Index, 0);
if Selection. cpMin >= 0 then Fmt := '%s'#13#10
else begin
Selection.cpMin :=SendMessage (RichEdit.Handle, EM_LINEINDEX, Index — 1, 0);
if Selection. cpMin < 0 then Exit;
L := SendMessage (RichEdit.Handle, EM_LINELENGTH, Selection. cpMin, 0);
if L = 0 then Exit;
Inc (Selection.cpMin, L);
Fmt := #13#10'%s';
end;
Selection.cpMax := Selection. cpMin;
SendMessage (RichEdit.Handle, EM_EXSETSEL, 0, Longint (@Selection));
Str := Format (Fmt, [S]);
SendMessage (RichEdit.Handle, EM_REPLACESEL, 0, LongInt (PChar (Str)));
if RichEdit. SelStart <> (Selection.cpMax + Length (Str)) then
raise EOutOfResources. Create (sRichEditInsertError);
end;
end;
Используя номер строки, перед которой мы собираемся произвести вставку, определяем номер символа, с которого начинается строка, и сохраняем его в переменной Selection.cpMin. Если номер символа не отрицательный, то это значит, что строка с таким номером существует и в конец добавляемой строки необходимо включить признак конца строки. Если номер символа отрицательный, значит, такой строки нет. Но возможно предпринимается попытка вставить после последней строки, так что запросим номер символа для предыдущей строки. Если и теперь номер отрицательный или длина этой строки нулевая, прекращаем дальнейшее выполнение. При благоприятном исходе увеличиваем Selection. cpMin на длину последней строки, таким образом, теперь она указывает на последний символ в тексте (сюда и будем вставлять). Наличие не нулевой длины у последней строки говорит о том, что строка не завершена. Чтобы завершить ее необходимо передать в текст сначала, признак конца строки, а потом саму добавляемую строку, по этому признак конца строки включается в ее начало. Далее устанавливаем конец выделения на начало выделения (переменная Selection. cpMax) и перемещаем туда курсор посылкой сообщения EM_EXSETSEL. Форматируем строку и отправляем сообщение EM_REPLACESEL для размещения вставляемой строки. После удачной вставки новое положение курсора должно ровняться положению перед вставкой плюс длина вставляемой строки (Selection.cpMax + Length (Str)). Если нет, то вызывается исключительная ситуация (raiseEOutOfResources.Create (sRichEditInsertError)).
Ошибка возникает потому, что сообщение EM_LINELENGTH использует только младшие 16 разрядов параметра. Этот параметр должен содержать номер любого символа из строки длину, которой надо определить. Если передается номер более 65 535, ну, например 65 536, то сообщение возвращает длину строки, содержащую символ с номером 0. Таким образом, в методе Add при запросе функции GetCount может быть получено значение на единицу большее, если попадется строка не нулевой длины. И тогда процедуре Insert будет передано значение на единицу большее, чем номер последней строки. Это первая ошибка.
Свойство Count, которое обращается к функции GetCount, может давать на одну строку больше при превышении размера текста в 65 535 байт. Ошибка возникает только если строка, содержащая усеченный номер символа, не пуста, то есть ошибка будет иметь плавающий характер.
Возвращаясь к методу Add, замечу, что эта ошибка не влияет на процедуру Insert. Потому что процедура Insert пробует не только указанную строку, но и, в случае не успеха, предыдущую, а это и будет как раз последней строкой в тексте. Но тут-то и кроется настоящая ошибка. Попав в эту ветвь if (т.е. после else) процедура Insert запрашивает длину строки с помощью сообщения EM_LINELENGTH и опять получает ошибочное значение. Обстоятельства нарастают как снежный ком, неотвратимо. Теперь неверная длина строки используется для определения номера последнего символа в тексте. А ведь длина настоящей последней строки возможно нулевая и это значит, что мы уже имеем номер последнего символа в тексте после предыдущего сообщения EM_LINEINDEX. После добавления к нему чужой длины, мы получаем номер символа заведомо выходящий за пределы текста. Из-за этого и произойдет вызов исключительной ситуации, но позже. Посылка сообщения EM_EXSETSEL с параметрами, выходящими за границы текста, проходит без катастрофических последствий, значение игнорируется, а курсор перемещается в конец текста. Сообщением EM_REPLACESEL добавляемая строка вставляется в конец текста. И вот, наконец, срабатывает цепь ошибок.
После вставки курсор перемещается за вставленную строку, т. е. в конец текста. Новое положение курсора не соответствует предыдущему плюс длина вставленной строки ровно на длину чужой строки, и это становится причиной вызова исключения.
Драма с грубыми ошибками и их игнорированием получает трагическое завершение из-за пустяка.
Избежать этой неприятной ситуации можно, если переопределить GetCount, Insert и Delete в классе TRichEditStrings, или исправить эти функции в исходных текстах, или написать свои.
Массив данных Array предоставляет возможность использования однои многомерных массивов (индексируемых последовательностей) данных.
Статические массивы задаются с фиксированным, неизменным размером. Они могут быть однои многомерными — последние являются массивами массивов (массивов и т. д.).
Длина и размерность такого многомерного массива всегда задается наивысшим, крайним левым массивом — родительским массивом.
Длина каждой размерности определяется двумя способами, которые могут легко сочетаться в многомерном массиве:
Индексный тип Здесь индекс — целый тип, обычно Byte или Word. Диапазон значений этого типа задает диапазон значений размерности. Например, тип Byte имеет диапазон значений от 0 до 255.
Порядковое.Порядковое В качестве альтернативы диапазон значений каждой размерности может быть задан явно порядковыми значениями, такими как 22.44.
Динамические массивы не имеют заранее выделенного участка памяти. При их определении создается только указатель. Для таких массивов должна быть задана их длина перед началом их использования.
Например, SetLength (dynArray, 5) задает одномерный массив dynArray длиной 5 элементов. Это действие выделяет память под массив. Все динамические массивы начинаются с индекса 0; каждый подмассив многомерных динамически массивов может иметь разный размер — он, конечно, является отдельным массивом.
После одной такой операции SetLength на элементы установленного массива можно ссылаться, даже если остальные массивы не определены.
И статические, и динамические массивы могут быть переданы подпрограммам как параметры. Если определение параметра-массива не имеет диапазона значений (т.е. это динамический массив), то вы должны, как это ни парадоксально, передать статический массив как параметр. Delphi передает длину как скрытый параметр в подпрограмму.
Открытый массив может также быть определен с квалификатором const. Он называется открытый массив с вариантами — он главным образом используется, чтобы позволить передавать в стандартную подпрограмму переменное число аргументов.
Для того, чтобы предать динамический массив по ссылке, массив и определение подпрограммы с параметром-массивом должны быть заданы через определение типа массива.
Описание используемых процедур и функций
Процедура btnOpenClick используется для отображения диалога открытия текстового файла.
Процедура btnSaveClick используется для отображения диалога сохранения текстового файла.
Процедура btnExitClick используется для закрытия программы без сохранения изменений.
Процедура FormCreate содержит в себе настройки для диалоговых окон открытия и сохранения файла.
Процедура btnAddDictonaryClick используется для отображения диалога открытия файла словаря. Кроме этого, в данной процедуре происходит чтение файла словаря и последующая обработка данных.
Процедура RichEdit1Change служит для мгновенной обработки введенных в поле RichEdit1 символов.
Описание структуры приложения и интерфейса пользователя
Форма Form1. Приложение является проектом, созданным на Delphi, имеет простой и понятный интерфейс, удобный как для опытного, так и для рядового пользователя:
Используются стандартные компоненты, такие, как RichEdit, OpenDialog, MainMenu, SaveDialog и StatusBar.
Программа не требует файла словаря для запуска или работы без подсветки ключевых слов, запускается в стандартной конфигурации.
Для открытия текстового файла, следует выбрать подменю «Открыть файл» из раздела меню «Файл». Для открытия текстового файла с расширением, отличным от *.txt, следует выбрать «Все файлы» в диалоговом окне открытия файла:
Программа может работать и как обычный текстовый редактор:
Для того чтобы включить выделение ключевых слов, достаточно открыть подменю «Подключить словарь» из меню «Настройки» и выбрать файл словаря, сохраненный в специальном расширении:
Ключевые слова не будут подсвечены до тех пор, пока не произойдет обновление RichEdit:
После загрузки словаря в StatusBar приложения отобразится информация о количестве загруженных слов. Ключевые слова подсвечиваются красным цветом и жирным начертанием:
Слова подсвечиваются по мере их набора. Части ключевых слов подсвечиваться не будут:
Кроме этого, не будут подсвечены и слова, включающие в себя ключевые, но не являющиеся ими, как на рисунке ниже:
Пользователь может сохранить измененный файл, выбрав подменю «Сохранить файл» в меню «Файл»:
Системные требования и имеющиеся ограничения
Программа работает под управлением операционных систем семейства Microsoft Windows NT с установленным.NET Framework (версии 2.0 и старше).
Для работы программы требуется 2 Мб дискового пространства и 128 Мб оперативной памяти.
Программа работает с данными, которые вводит сам пользователь, либо загруженные из файла. Программа частично предохранена от некорректного ввода данных, поэтому при неправильном вводе поведение программы является непредвиденным.
Размер словаря ограничен 200 словами. При вводе большего количества слов программа может работать некорректно.
Программа не требует защиты и может свободно распространяться.
Результаты тестирования приложения
Для тестирования приложения откроем прилагающийся текстовый файл, подключим словарь и обновим RichEdit1:
Анализ временных характеристик и выводы. Тест 1
Количество слов | Размер словаря | Количество ошибок | Время работы программы, мс | |
Анализ временных характеристик и выводы. Тест 2
Количество слов | Размер словаря | Количество ошибок | Время работы программы, мс | |
Таблицы позволяют понять, что программа работает медленнее при большем количестве слов для проверки. Однако, при сохранении объема проверки и увеличении размера словаря, скорость выполнения программы падает медленнее. Из этого можно сделать вывод, что программа будет работать максимально быстро при малом размере словаря и относительно небольших по объему текстовых файлов.
приложение подсвечивание ключевой слово
Заключение
В соответствии с заданием, мною была разработана программа, имеющая простой для пользователя интерфейс и выполняющая ввод данных самостоятельно, либо непосредственно загружает данные из файла. В ходе работы были рассмотрены алгоритмы работы с файлами. Кроме того, были подробно описаны необходимые для хранения структуры данных. Составленная программа полностью соответствует техническому заданию.
1. Алгоритмы и структуры данных: учеб. пособие/Л.Г. Гагарина, В. Д. Колдаев. — М., 2009. — 309 стр.
2. Краткое описание класса TRichEdit / Онлайн-учебник по языку С++ URL: http://articles.org.ru/lection/richedit.php
3. TStringList — Тип. Справочник Delphi / Онлайн-учебник по языку С++ URL: http://www.delphibasics.ru/TStringList.php
4. Работа с файлами C++/ Онлайн-учебник по языку С++ URL: http://subscribe.ru/archive/comp.soft.prog.prognull.prognullcpp/200 710/29111249.html
Приложение
unit notebad;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Menus, ComCtrls;
type
TForm1 = class (TForm)
MainMenu1: TMainMenu;
File1: TMenuItem;
btnOpen: TMenuItem;
btnSave: TMenuItem;
N2: TMenuItem;
btnExit: TMenuItem;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
N1: TMenuItem;
btnAddDictonary: TMenuItem;
RichEdit1: TRichEdit;
OpenDialog2: TOpenDialog;
StatusBar1: TStatusBar;
procedure btnOpenClick (Sender: TObject);
procedure btnSaveClick (Sender: TObject);
procedure btnExitClick (Sender: TObject);
procedure FormCreate (Sender: TObject);
procedure btnAddDictonaryClick (Sender: TObject);
procedure RichEdit1Change (Sender: TObject);
private
{Private declarations}
public
{Public declarations}
end;
var
Form1: TForm1;
word: array [1.200] of string;
implementation
{$R *.dfm}
procedure TForm1. btnOpenClick (Sender: TObject);
begin
if OpenDialog1. Execute then
begin
Form1.Caption := OpenDialog1. FileName;
RichEdit1.Lines.LoadFromFile (OpenDialog1.FileName);
RichEdit1.SelStart := 0;
StatusBar1.Panels[0]. Text := 'Файл загружен';
btnSave.Enabled:=true;
end;
end;
procedure TForm1. btnSaveClick (Sender: TObject);
begin
SaveDialog1.FileName := Form1. Caption;
if SaveDialog1. Execute then
begin
RichEdit1.Lines.SaveToFile (SaveDialog1.FileName + '.txt');
Form1.Caption := SaveDialog1. FileName;
StatusBar1.Panels[0]. Text := 'Файл сохранен';
end;
end;
procedure TForm1. btnExitClick (Sender: TObject);
begin
Application.Terminate;
end;
procedure TForm1. FormCreate (Sender: TObject);
begin
with OpenDialog1 do
begin
Options := Options + [ofPathMustExist, ofFileMustExist];
InitialDir := ExtractFilePath (Application.ExeName);
Filter := 'Текстовые файлы (*.txt)|*.txt|Все файлы (*)|*';
end;
with OpenDialog2 do
begin
Options := Options + [ofPathMustExist, ofFileMustExist];
InitialDir := ExtractFilePath (Application.ExeName);
Filter := 'Файл словаря (*.nbdic)|*.nbdic';
end;
with SaveDialog1 do
begin
InitialDir := ExtractFilePath (Application.ExeName);
Filter := 'Текстовый файл (*.txt)|*.txt';
end;
end;
procedure TForm1. btnAddDictonaryClick (Sender: TObject);
var
i: integer;
strList: TStringList;
begin
if OpenDialog2. Execute then
begin
strList:=TStringList.Create;
strList.LoadFromFile (OpenDialog2.FileName);
StatusBar1.Panels[0]. Text := 'Словарь загружен. Добавлено слов: ' + IntToStr (strList.Count);
for i:=0 to strList. Count-1 do
word[i]: =strList[i];
strList.free;
btnSave.Enabled:=false;
end;
end;
procedure TForm1. RichEdit1Change (Sender: TObject);
var
toEnd, foundAt, curPos, i: integer;
begin
RichEdit1.OnChange := nil;
RichEdit1.Lines.BeginUpdate;
curPos := RichEdit1. SelStart;
try
RichEdit1.SelectAll;
RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style — [FsBold];
RichEdit1.SelAttributes.Color := clBlack;
for i := Low (word) to High (word) do
begin
foundAt := RichEdit1. FindText (word[i], 0, Length (RichEdit1.Text), [StWholeWord]);
while foundAt <> -1 do
begin
RichEdit1.SelStart := foundAt;
RichEdit1.SelLength := Length (word[i]);
RichEdit1.SelAttributes.Style := RichEdit1.SelAttributes.Style + [FsBold];
RichEdit1.SelAttributes.Color := clRed;
foundAt := RichEdit1. FindText (word[i], foundAt + 1, Length (RichEdit1.Text), [StWholeWord]);
end;
end;
RichEdit1.SelStart := curPos;
finally
RichEdit1.Lines.EndUpdate;
RichEdit1.OnChange := RichEdit1Change;
end;
end;
end.