Классификация по модели данных
Скачать 2.86 Mb.
|
2.3 Структура, архитектура программного продукта Центральное место в объектно-ориентированном программировании занимает разработка логической модели системы в виде диаграммы классов. Диаграмма классов (class diagram) служит для представления статической структуры модели системы в терминологии классов объектно-ориентированного программирования. Диаграмма классов может отражать, в частности, различные взаимосвязи между отдельными сущностями предметной области, такими как объекты и подсистемы, а также описывать их внутреннюю структуру и типы отношений. Класс (class) в данной диаграмме служит для обозначения множества объектов, которые обладают одинаковой структурой, поведением и отношениями с объектами других классов. Графически класс изображается в виде прямоугольника, который дополнительно может быть разделен горизонтальными линиями на разделы или секции. В этих разделах могут указываться имя класса, атрибуты (переменные) и операции (методы). Кроме внутреннего устройства или структуры классов на соответствующей диаграмме указываются отношения между классами: Для данного приложения были выделены классы, описанные в таблице 2.2. Таблица 2.2 - Описание классов, используемых в программном продукте
Диаграмма классов приложения-конвертера показана на рисунке 2.6. Из диаграммы видно, что класс MyCode является переменной класса Template. Класс Template содержит следующие поля: dt, lv, thisTemplate, mycode, fs, sr, sw, correct, masCode, masPerem, masPeremCount, masSost, masCodeLength. dt - переменная типа DataTable, содержащая информацию, хранящуюся в базе данных; lv - переменная типа ListView, объект интерфейса, в который записываются сообщения об ошибках в шаблонах; thisTemplate - переменная типа string, означающая название шаблона, который обрабатывается в данный момент; mycode - массив класса MyCode, хранящий информацию о всех фрагментах кода обнаруженных в данном шаблоне; fs - переменная типа FileStream, определяющая с каким файлом будет работать программа; sr - переменная типа StreamReader, определяющая из какого файла будет считана информация; sw - переменная типа StreamWriter, определяющая в какой файл будет записываться информация; correct - переменная типа bool, указывающая на то корректно ли обработан текущий фрагмент кода; masCode - массив типа string, содержащий все найденные строки кода в шаблоне; masCodeLength - переменная типа int, указывающая сколько строк кода найдено в шаблоне; masPerem - двумерный массив типа string, содержащий название и значение созданных переменных; masPeremCount - переменная типа int, указывающая сколько переменных создано в данный момент; masSost - массив типа int, содержащий список состояний автомата для текущей строки кода. Так же класс содержит следующие методы: Connect, Search, Analize, Check, ExecuteCode. Метод Connect осуществляет подключение к шаблону по заданному пути. Метод Search находит фрагменты кода в шаблоне. Метод Analize определяет состояния для строки кода. Метод Check является рекурсивным, он определяет логическую правильность строки. Метод ExecuteCode исполняет текущий шаблон. Для описанных классов можно составить диаграмму классов[3]. 2.4 Функциональная схема, функциональное назначение программного продукта В программном продукте разработаны два возможных варианта обработки информации с уникальными алгоритмами. Если пользователь использует кодовые шаблоны ему сначала необходимо указать входные файлы, затем либо создать новый шаблон, либо выбрать уже существующий. Далее пользователь указывает директорию и имя выходного файла и запускает процесс конвертации. Во время этого процесса изначально происходит выделение участков кода в шаблоне, затем в каждом из участков выделяются лексемы, после этого определяется их логический смысл и исполняется шаблон. Если на каком-то из этих этапов возникли ошибки, то информация о них заносится в журнал ошибок. После исполнения всех шаблонов пользователь может использовать выходной файл. Если использовать шаблоны, созданные при помощи конструктора, то пользователю необходимо указать базу данных, которую требуется конвертировать, затем указать директорию выходного файла, создать шаблон и запустить процесс конвертации. Сама конвертация состоит из двух частей: создание таблицы истинности на основе шаблона и исполнение конвертации согласно таблице истинности. Затем выходной файл может использоваться пользователем по назначению. 2.5 Выводы Во второй главе выпускной квалификационной работы были выбраны средства разработки, а именно Microsoft Visual Studio 2008, были описаны основные методы реализации программного продукта, а так же описана его структура. Так же была описана функциональная схема программного продукта. Основными пунктами рассмотрения во второй главе стали: - выбор методов и средств для реализации, его обоснование; - описание применяемых алгоритмов; - структура, архитектура программного продукта; - функциональная схема, функциональное назначение программного продукта. 3. РЕАЛИЗАЦИЯ И ТЕСТИРОВАНИЕ ПРОГРАММНОГО ПРОДУКТА .1 Описание реализации Одной из сложностей реализации данного программного продукта является написание алгоритма распознавателя. Весь алгоритм описывается методами: Search, Analize, Check, ExecuteCode. Метод Search считывает шаблон и находит фрагменты кода, выделенные символами «*» с обеих сторон, и записывает их в массив [1]. void Search() { char c;s = "";sign = false;(!sr.EndOfStream) {= Convert.ToChar(sr.Read());((c != '*') && (sign == true)) { s += c.ToString(); }((c == '*') && (sign == false)) { sign = true;= ""; }((c == '*') && (sign == true)) { if (s != "") { sign = false;[masCodeLength] = s;++; }+= c.ToString(); }}= new MyCode[masCodeLength];} Метод Analize разбивает строку кода на отдельные лексемы и определяет состояние для каждой из них, в случае если используются символы или слова, не предусмотренные языком, или некорректные названия переменных - в журнал ошибок добавляется соответствующее сообщение об ошибке. Полный список используемых лексем представлен в таблице 2.1. void Analize() { string[] masIdent = new string[1024];masIdentLength = 0;s = "";c;sign = true;(int a = 0; a < masCodeLength; a++) { correct = false;= 0;[a] = masCode[a].Trim();[a] = masCode[a].ToLower();= "";(int b = 0; b < masCode[a].Length; b++) { c = masCode[a][b];(c != ' ')(c == '.') { if (s != "") { masIdent[masIdentLength] = s;++; }[masIdentLength] = ".";++;= ""; } …((c == ' ') && (s != "")) { masIdent[masIdentLength] = s;++;= ""; } }(s != "") { masIdent[masIdentLength] = s;++; }[a] = new MyCode("", null);(int z = 0; z < masIdentLength; z++)[a].code += masIdent[z] + " ";= new int[masIdentLength]; В предыдущей части метода все найденные лексемы были записаны в массив masIdent, далее инициализируется цикл, в котором для всех найденных лексем определяется состояние и записывается в массив masSost. (int b = 0; b < masIdentLength; b++) { int Value;(masIdent[b] == "для")[b] = 2;if (masIdent[b] == "до")[b] = 3;if (masIdent[b] == "столбец")[b] = 4;if (masIdent[b] == "строка")[b] = 5; …(Char.IsLetter(masIdent[b][0])) { bool f = true;(int d = 1; d < masIdent[b].Length; d++)(!Char.IsLetterOrDigit(masIdent[b][d]))= false;(f == true) masSost[b] = 1; else Добавление записи об ошибке в журнал ошибок, в случае, если, найденного идентификатора, не существует. { lv.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[a].code);.Items[lv.Items.Count - 1].SubItems.Add("Неопознанный идентификатор " + masIdent[b]); }} else { lv.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[a].code);.Items[lv.Items.Count - 1].SubItems.Add("Неопознанный идентификатор " + masIdent[b]);}} [a] = new MyCode(mycode[a].code, masSost);(0, masSost[0], a); } Метод Check основан на работе нисходящего распознавателя с возвратом: определяется текущее состояние, если оно возможно, то осуществляется переход в следующее. Если невозможно, то состояние переключается в альтернативное, если такого нет, то в журнал ошибок добавляется сообщение об ошибке [5]. public void Check(int a, int s, int indc) { if (masSost[a] == s) { if ((s == 1) && (a == 0)) { if (a == masSost.Length - 1)= true; } else((s == 2) && (a == 0)) s = 1; else(((s == 4) || (s == 5)) && (a == 0)) s = 8; else((s == 1) && (a == 1)) s = 9; else((s == 8) && (a == 1)) s = 6; else((s == 10) && (a == 1)) s = 1; else((s == 9) && (a == 2)) s = 12; else((s == 6) && (a == 2)) { if (a == masSost.Length - 1)= true; } else(((s == 1) || (s == 12)) && (a == 2)) s = 11; else((s == 12) && (a == 3)) s = 3; else((s == 11) && (a == 3)) s = 8; else((s == 3) && (a == 4)) s = 12; else((s == 8) && (a == 4)) { if (masSost[0] == 4)= 7; …((s == 6) && (a == 7)) { if (a == masSost.Length - 1) correct = true; }(((s == 12) || (s == 1)) && (a == 7))= 11;((s == 11) && (a == 8)) { if (a == masSost.Length - 1)= true; } В случае если массив входных состояний прошел проверку распознавателем и все состояния совпали, переменной correct присваивается значение true, а в случае, если где-нибудь нашлось несовпадение, то осуществляется возврат и проверяется для альтернативного состояния. (correct == false) { a++;(a, s, indc); } }((s == 8) && (a == 1)) { s = 10;(a, s, indc); }((s == 1) && (a == 2)) { s = 12;(a, s, indc);} …((s == 1) && (a == 7)) { s = 12;(a, s, indc); } Если осуществляемый переход не был распознан, то, обрабатываемая строка кода, считается логически неверной и добавляется соответствующая запись об ошибке в журнал ошибок. (correct == false) {.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(masCode[indc]);.Items[lv.Items.Count - 1].SubItems.Add("Нарушен логический смысл строки");[indc].correct = false; } } Метод ExecuteCode записывает содержимое шаблона в выходной файл, исполняя программные строки. Если встречается цикл, то создается временный файл, в который записывается содержимое цикла и пока цикл не завершится, исполняется содержимое этого файла. Это необходимо для выполнения вложенных циклов. void ExecuteCode(int NCode, bool cikcle, StreamReader sr, StreamWriter sw, int tempF) {c;(!sr.EndOfStream) {= Convert.ToChar(sr.Read()); Алгоритм считывает по символу входной файл, в случае если встречается закрывающаяся фигурная скобка, означающая конец цикла, и переменная cikcle равна true, то это означает, что метод был вложенным и завершает его [4]. ((c == '}') && (cikcle == true)) {; } Если, считанный символ, не был «*», то это означает, что символ не относится к кодовым командам и его необходимо просто вывести. (c != '*') {.Write(c); } Если, считанный символ, был «*», алгоритм считывает следующий символ, если и он «*», то это означает что пользователь, хотел вывести этот символ в выходной файл. (c == '*') { c = Convert.ToChar(sr.Read());(c == '*').Write(c); Если, следующий символ не был «*», то это означает, что все последующие символы до «*» относятся к кодовым командам. (mycode[NCode].correct == true)(mycode[NCode].masSost[0] == 1) { bool creat = false;(int a = 0; a < masPeremCount; a++) { if (masPerem[a, 0] == mycode[NCode].code) { creat = true;.Write(masPerem[a, 1]);++;(sr.Read() != '*') { }break; } } Если, в коде, пользователь пытается вывести переменную, которая до этого не была объявлена, то запись об ошибке заносится в журнал ошибок и дальний код уже не исполняется [10]. (creat == false) { if (mycode[NCode].code != "") { lv.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Попытка вывести несуществующую переменную");[NCode].code = ""; }++;(sr.Read() != '*') { } } }(mycode[NCode].masSost[0] == 4) { if (mycode[NCode].masSost[2] == 6) { NCode++;.Write(dt.Columns.Count.ToString());(sr.Read() != '*') { } }(mycode[NCode].masSost[2] == 12) {(Convert.ToInt32(mycode[NCode].masValue[2]) < dt.Columns.Count) { if (mycode[NCode].masSost[5] == 7).Write(dt.Columns[Convert.ToInt32(mycode[NCode].masValue[2])].DataType.Name);.Write(dt.Columns[Convert.ToInt32(mycode[NCode].masValue[2])].ColumnName);} Если, пользователь пытается обратиться к столбцу или строке, которых не существует, то соответствующая запись об ошибке добавляется в журнал ошибок. {.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Индекс выходит за рамки массива");[NCode].code = ""; }++;(sr.Read() != '*') { }} { bool creat = false;(int a = 0; a < masPeremCount; a++)(masPerem[a, 0] == mycode[NCode].masValue[2]) { creat = true;(Convert.ToInt32(masPerem[a, 1]) < dt.Columns.Count) { if (mycode[NCode].masSost[5] == 13).Write(dt.Columns[Convert.ToInt32(masPerem[a, 1])].ColumnName);.Write(dt.Columns[Convert.ToInt32(masPerem[a, 1])].DataType.Name); } else {.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Индекс выходит за рамки массива");[NCode].code = ""; }++;(sr.Read() != '*') { } }(creat == false) { В случае если пользователь указывает несуществующую переменную в качестве индекса столбца или строки, запись о соответствующей ошибке добавляется в журнал ошибок. .Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Попытка обратиться к несуществующей переменной");++;(sr.Read() != '*') { } } }}(mycode[NCode].masSost[0] == 5) { int n1 = 0, n2 = 0, nn = 0;(mycode[NCode].masSost[2] == 6) { sw.Write(dt.Rows.Count.ToString());++;(sr.Read() != '*') { } } { if (mycode[NCode].masSost[2] == 12) { { n1 = Convert.ToInt32(mycode[NCode].masValue[2]);++; }(mycode[NCode].masSost[7] == 12) { n2 = Convert.ToInt32(mycode[NCode].masValue[7]);++; } { bool creat = false;(int a = 0; a < masPeremCount; a++)(masPerem[a, 0] == mycode[NCode].masValue[7]) { creat = true;= Convert.ToInt32(masPerem[a, 1]);++; } ... В случае если переменная использовалась в качестве индекса столбца или строки и ее значение превышает число столбцов или строк таблицы соответственно, то запись об этой ошибке добавляется в журнал ошибок. Else { if (n1 >= dt.Rows.Count) { if (mycode[NCode].code != "") {lv.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Индекс " + n1 + " выходит за рамки массива");}} (n2 >= dt.Columns.Count) { if (mycode[NCode].code != "") { lv.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Индекс " + n2 + " выходит за рамки массива"); }}[NCode].code = ""; }++;((sr.Read() != '*') && (!sr.EndOfStream)) { } } } } else(mycode[NCode].masSost[0] == 2) { int k, nk;[masPeremCount, 0] = mycode[NCode].masValue[1];[masPeremCount, 1] = mycode[NCode].masValue[3];= masPeremCount;++;(mycode[NCode].masSost[5] == 12)= Convert.ToInt32(mycode[NCode].masValue[5]); else(mycode[NCode].masSost[5] == 4) k = dt.Columns.Count;k = dt.Rows.Count;(sr.Read() != '*') { } В случае если пользователь объявил цикл и после этого не указал его начало, не поставив «{», это расценивается как ошибка и запись об этом добавляется в журнал ошибок. = Convert.ToChar(sr.Read());(c == '\r')= Convert.ToChar(sr.Read());(c == '\n')= Convert.ToChar(sr.Read());(c != '{') { lv.Items.Add(thisTemplate, thisTemplate);.Items[lv.Items.Count - 1].SubItems.Add(mycode[NCode].code);.Items[lv.Items.Count - 1].SubItems.Add("Не указано начало цикла");[NCode].correct = false;++; } else { Если начало цикла указано корректно, то переменная, отвечающая за глубину вложенности циклов, увеличивается на единицу, содержимое цикла, до знака «}», записывается во временный файл, создается дубликат массивов содержащих значения переменных и данный рекурсивный метод запускается уже для этого временного файла [2]. ++;.CreateDirectory("Temp");sw1=new StreamWriter("Temp\\Temp"+tempF.ToString()+".txt", false, Encoding.UTF8);= Convert.ToChar(sr.Read());(c == '\r') { sr.Read();= Convert.ToChar(sr.Read()); }cickleCount = 0;(c != '}') { if (c == '{')++;.Write(c);(sr.EndOfStream);= Convert.ToChar(sr.Read());((c == '}') && (cickleCount != 0)) { cickleCount--;.Write(c);= Convert.ToChar(sr.Read()); } }.Close();sr1 = new StreamReader("Temp\\Temp" + tempF.ToString() + ".txt", Encoding.UTF8);CickleCode = 0;s1 = "";sign2 = false;(!sr1.EndOfStream) { c = Convert.ToChar(sr1.Read());((c != '*') && (sign2 == true)) { s1 += c.ToString(); }((c == '*') && (sign2 == false)) { sign2 = true;= ""; }((c == '*') && (sign2 == true)) { if (s1 != "") { sign2 = false;++; }s1 += c.ToString(); } }.Close();(int a = Convert.ToInt32(mycode[NCode].masValue[3]); a < k; a++) { masPerem[nk, 1] = a.ToString();(NCode + 1, true, new StreamReader("Temp\\Temp" + tempF.ToString() + ".txt", Encoding.UTF8), sw, tempF);} После завершения работы цикла временный файл удаляется, а глубина вложенности циклов уменьшается на единицу. [nk, 0] = "";[nk, 1] = "";= CickleCode + 1;.Delete("Temp\\Temp" + tempF.ToString() + ".txt");-; } } Алгоритм работы с шаблонами, созданными при помощи конструктора, описывается методами: Execute, GetTruthTable, ExecuteTag. Метод Execute вызывается один раз при запуске обработки шаблона. Этот метод является внешним и из него вызываются остальные методы. Изначально метод создает выходной файл и «Таблицу истинности» [11]. truthdt=new DataTable();sw=new StreamWriter(textBox4.Text+"Выходной файл.xml",false,Encoding.UTF8);(int a = 0; a <= dt.Columns.Count;a++ ).Columns.Add("",typeof(bool));(int a = 0; a <= dt.Rows.Count; a++) { DataRow dr = truthdt.NewRow();(int b = 0; b < dr.ItemArray.Length; b++)[b] = true;.Rows.Add(dr); } После этого начинает исполняться цикл, выделяющий каждый тег, определяющий его тип, и в зависимости от этого исполнять его. (!complete) { tagind = GetTagIndex(Iitems);(mastag[tagind].type == types.global || mastag[tagind].type == types.main) {… }(mastag[tagind].type == types.block) {… }(mastag[tagind].type == types.simple) {… }(Iitems >= itemscount)= true; }.Close(); } В случае если тип тега глобальный или основной, содержимое тега просто записывается в файл. (mastag[tagind].name != "") sw.WriteLine(items[Iitems]); Iitems++; Если тип тега блочный, то список всех тегов принадлежащих этому фрагменту записываются в отдельный массив, а так же определяется, есть ли в этом списке теги, требующие для вывода значений строковую индексацию. bool haveRow = false;[] blocktag = new tag[mastag[tagind].indF - mastag[tagind].indS - 1];(int a = mastag[tagind].indS + 1, b = 0; a < mastag[tagind].indF; a++, b++) { blocktag[b] = mastag[GetTagIndex(a)];(blocktag[b].type == types.simple)(blocktag[b].source == "индекс строки" || blocktag[b].source == "содержимое ячейки")= true;} После этого создается «таблица истинности» и к ней применяются все условия простых тегов найденных в списке. =CreateTable(truthdt,dt);(int a = 0; a < blocktag.Length; a++)(blocktag[a].type == types.simple) truthdt = GetTruthTable(dt, truthdt, blocktag[a]); Далее в зависимости от того были ли найдены теги использующие строковую индексацию создается либо только цикл по столбцам, либо циклы по столбцам и по строкам, в которых исполняются все встречающиеся теги, за исключение блочных. (haveRow) {(int a = 0; a < dt.Rows.Count; a++)(int b = 0; b < dt.Columns.Count; b++) {wasEx = false;swt = new StreamWriter("temp.txt", false, Encoding.UTF8);(int c = 0; c < blocktag.Length; c++) {(blocktag[c].type == types.global)(blocktag[c].name != "").WriteLine(items[mastag[tagind].indS + c + 1]);(blocktag[c].type == types.simple)(blocktag[c].name != "")=ExecuteTag(dt, truthdt, blocktag[c], a, b, swt); }.Close();(wasEx) { StreamReader sr = new StreamReader("temp.txt", Encoding.UTF8);.Write(sr.ReadToEnd());.Close();.Delete("temp.txt"); } } } { for (int a=0;a { if (blocktag[c].type == types.global)(blocktag[c].name != "").WriteLine(items[mastag[tagind].indS + c + 1]);(blocktag[c].type == types.simple)(blocktag[c].name != "")(dt, truthdt, blocktag[c], 0, a, sw); } } Если тип тега простой, то для него создается новая «Таблица истинности» и тег исполняется. if (mastag[tagind].type == types.simple) { truthdt=CreateTable(truthdt,dt);tempdt = GetTruthTable(dt, truthdt, mastag[tagind]);(mastag[tagind].name!="")(dt, tempdt, mastag[tagind], 0, 0, sw);++; } Метод GetTruthTable применяет условия к «Таблице истинности». В качестве аргументов принимает таблицу со значениями из базы данных, уже созданную «Таблицу истинности» и тег, условие которого нужно обработать. Метод ExecutTag исполняет простой тег. В качестве аргументов принимает таблицу данных, «Таблицу истинности», тег, индекс строки, индекс столбца и поток записи в файл. |