Welcome to
aleprojects.com

Парсер текста. Delphi класс TAleSimpleParser (Beta version)

Класс TAleSimpleParser позволяет выделять из текстовой строки такие фрагменты, как числа (целые десятичные, шестнадцатеричные, с плавающей запятой), текстовые константы (текст, заключенный в кавычки), скобки, списки и др. и, наоборот, пропускать пробелы и комментарии.

Выделенные из строки фрагменты помещаются в двунаправленный список в порядке своего следования.

Скачать

Класс TAleSimpleParser имеет следующие основные свойства и методы:

Свойство Start:PWideChar;
содержит указатель на текстовую строку, подлежащую обработке. Строка является обычным массивом символов, заканчивающимся #0.

function ParseText(var Res:PWideChar; Len:LongInt=-1):TAleTextElement;
метод осуществляющий обработку текста, заданного свойством Start. Возвращает список (о классе TAleTextElement см.ниже) с выделенными фрагментами или nil в случае ошибки.
В параметре Res возвращается указатель на символ, до которого текст был обработан. Обработка текста может быть завершена при достижении последовательности символов, определенных как "конец текста" (см. описание свойства TextEnd ниже). Если данной последовательности в обрабатываемой строке нет, то обрабатываются все Len символов строки. Если Len отрицательно, то обрабатываются все символы до завершающего строку #0.

...
const 
wsEnd:WideString = ';';

var List: TAleTextElement;
    Parser: TAleSimpleParser;
    W: WideString;
    S: PWideChar;
...
W := '12.1, "text", 0xFF ;10 a';

Parser := TAleSimpleParser.Create;
Parser.Start := PWideChar(W);
Parser.TextEnd := PWideChar(wsEnd);
List := Parser.ParseText(S);

//List - список с результатом
//S - указывает на '10 a'
...

Свойства Quotes, Brackets, TextEnd, ListSeparator, PairSeparator, BlockComment, StreamCommentStart, StreamCommentEnd, Blanks (все имеют тип PWideChar) определяют символы кавычек текстовых констант ( ' ' " " “ ” ‘ ’ « »), символы скобок ( ()[]{} ), окончание текста ( ; ), разделитель элементов списка ( , ), разделитель "пары" ( = ), блоковый комментарий ( // ), начало потокового комментария ( /* ), окончание потокового комментария ( */ ) и символы, считающиеся пробелами ( #32#13#10#9 ), соответственно. В скобках приведены значения свойств устанавливаемые по-умолчанию при создании экземпляра класса, за исключением свойств TextEnd и PairSeparator, их значения по-умолчанию равны nil.
Свойства Quotes и Brackets содержат строки с парами символов. Первый символ в паре - открывающий (кавычка или скобка), второй - закрывающий. Свойство Blanks определяет набор символов, рассматриваемых как пробелы. Остальные свойства могут содержать произвольные строки, необязательно одиночные символы.

Формат строк для этих свойств должен соответствовать формату WideString.

В случае возникновения ошибки при обработке текста свойства ErrorCode, ErrorPointer, ErrorLine, ErrorPosition содержат соответственно код ошибки, указатель на символ с ошибкой, номер строки с ошибкой, позицию ошибки в строке. Все свойства, за исключением ErrorPointer, имеют тип LongInt. Свойство ErrorPointer имеет тип PWideChar.
В случае удачного завершения свойство ErrorCode равно нулю.

Свойство Options:LongInt;
Установкой соответствующих битов задаются различные параметры парсера такие как, например, воспринимать ли '-' как часть числа, допускаются ли escape-команды (\n, \t, \u0410) в текстовых константах и т.д.

Определены следующие константы:
ale_opt_AllowUnrecognizedText = $00000001;  //Допускаются неопознанные фрагменты. Иначе ошибка.
ale_opt_MinusAsPartOfNumber = $00000002;    //Рассматривать '-' как часть числа.
ale_opt_AllowAlphaAfterNumber = $00000004;  //Допускается ли буквенно-цифровой символ после числа.
ale_opt_AllowEscapesInConst = $00000010;    //Допускаются ли в текстовых константах \n \t \uXXXX \r и т.д.
ale_opt_TextEndRequired = $00000020;        //В тексте обязательно должно присутствовать окончание. Иначе ошибка.
ale_opt_FirstCall = $80000000;
ale_opt_Parameters = $7FFFFFFF;
ale_opt_Default = ale_opt_AllowUnrecognizedText;

Результат работы метода TAleSimpleParser.ParseText

Фрагменты, выделенные из текста методом ParseText, хранятся в двунаправленном списке. Каждый элемент списка представляет собой экземпляр класса TAleTextElement (или класса-потомка). Метод ParseText возвращает указатель на первый элемент созданного списка.

Схематичное представление списка

Класс TAleTextElement имеет следующие основные свойства и методы:

Свойства Next, Prev - указатели на следующий и предыдущий элемент списка. Значение свойства Next последнего элемента списка равно nil. Свойство Prev первого элемента списка содержит указатель на последний элемент списка.

Свойство SubElements - каждый элемент списка в свою очередь может ссылаться на подобный двунаправленный список. Свойство SubElements содержит указатель на первый элемент списка.

Свойство Parent - содержит указатель на родительский элемент.

Метод Add - добавляет элемент в конец списка.
function Add(E: TAleTextElement): TAleTextElement;
Элемент E в свою очередь сам может быть списком.

Свойство Start:PWideChar - содержит указатель на первый символ выделенного из текста фрагмента.

Свойство Length:LongInt - содержит длину выделенного из строки фрагмента.

Например для следующего текста ' 12.32E-5 "Some text" 0xFF'
значения Start и Length элементов списка будут такими:
1ый элемент: Start^ = '1', Length = 8, TypeOfElement = ale_ate_floatnumber
2ой элемент: Start^ = '"', Length = 11, TypeOfElement = ale_ate_textconst
3ий элемент: Start^ = '9', Length = 4, TypeOfElement = ale_ate_hexnumber

Свойство TypeOfElement:LongInt - определяет тип распознанного фрагмента (целое десятичное число, шестнадцатеричное число, число с плавающей точкой, текстовая константа, скобки, список и т.д., в том числе пользовательские типы).

Определены следующие константы:
ale_ate_unknowntext = 0;    //неопознанный фрагмент
ale_ate_textconst = 1;      //текстовая константа
ale_ate_atom = 2;           //последовательность букв и цифр (для будущего использования)
ale_ate_date = 5;           //дата-время (для будущего использования)
ale_ate_decnumber = 10;     //десятичное целое число
ale_ate_hexnumber = 11;     //шестнадцатеричное число
ale_ate_floatnumber = 12;   //число с плавающей точкой
ale_ate_operator = 100;     //оператор (для будущего использования)
ale_ate_parentheses = 1000; //скобки
ale_ate_brackets = 1000;    //скобки
ale_ate_list = 1100;        //список
ale_ate_listelement = 1110; //элемент списка
ale_ate_pair = 1200;        //пара, элемент вида a=b или a:b
ale_ate_structure = 1300;   //структура, элемент вида abс(x,y) (для будущего использования)
ale_ate_tag  = 1500;        //тег (xml, html) (для будущего использования)
ale_ate_tagattr = 1510;     //атрибуты тега (для будущего использования)
ale_ate_tagcdata = 1520;    //CDATA (для будущего использования)

Свойство Size:LongInt - содержит фактическую длину выделенного текстового фрагмента. Отличается от значения Length только для текстовых констант.

Например, для константы '<a href=\'www.google.com\'>' значение свойства Length будет равно 29, а значение свойства Size будет равно 25.

Свойство Literal:WideString - возвращает характерные символы выделенного текстового фрагмента. Для текстовых констант будет возвращен символ открывающей кавычки, для скобок - символ открывающей скобки, для списка - разделитель элементов списка, для пар - разделитель пары. Свойство вычисляется на основе свойств LiteralStart:PWideChar и LiteralLength:LongInt. Для остальных текстовых элементов LiteralStart=Start и LiteralLength=Length.

Свойство Value:WideString - возвращает текстовый фрагмент исходя из значений свойств Start и Length (берется Length символов начиная с позиции Start). Имеется отличие для текстового фрагмента типа текстовая константа - константа возвращается не в виде своего представления, а в том виде, в котором она должна храниться в памяти.

Для константы '<a href=\'www.google.com\'>\nNewTextLine' свойство Value возвратит строку <a href='www.google.com'>#13NewTextLine

Представление скобок, списков и пар

Скобки, списки и пары представляются одним элементом двунаправленного списка. Само содержимое скобок, списков и пар хранится в подчиненном данному элементу списке.

Соответствующий для строки '(а 10.7) 'a'' список будет выглядеть следующим образом:
1й элемент: Start^='(', Length=8, Size=8, TypeOfElement = ale_ate_brackets
    1й субэлемент: Start^='a', Length=1, Size=1, TypeOfElement = ale_ate_unknowntext
    2й субэлемент: Start^='1', Length=4, Size=4, TypeOfElement = ale_ate_floatnumber
2й элемент: Start^='''', Length=3, Size=1, TypeOfElement = ale_ate_textconst

Соответствующий для строки '10.7, 'aa', 2 a' список будет выглядеть следующим образом:
1й элемент: Start^='1', Length=15, Size=15, TypeOfElement = ale_ate_list
    1й субэлемент: Start^='1', Length=5, Size=5, TypeOfElement = ale_ate_listelement
        1й субэлемент: Start^='1', Length=4, Size=4, TypeOfElement = ale_ate_floatnumber
    2й субэлемент: Start^='''', Length=5, Size=5, TypeOfElement = ale_ate_listelement
        1й субэлемент: Start^='''', Length=4, Size=2, TypeOfElement = ale_ate_textconst
    3й субэлемент: Start^='''', Length=3, Size=3, TypeOfElement = ale_ate_listelement
        1й субэлемент: Start^='2', Length=1, Size=1, TypeOfElement = ale_ate_decnumber
        2й субэлемент: Start^='a', Length=1, Size=1, TypeOfElement = ale_ate_unknowntext

Соответствующий для строки 'a=12 b='32'' список будет выглядеть следующим образом:
1й элемент: Start^='a', Length=4, Size=4, TypeOfElement = ale_ate_pair
    1й субэлемент: Start^='a', Length=1, Size=1, TypeOfElement = ale_ate_unknowntext
    2й субэлемент: Start^='1', Length=2, Size=2, TypeOfElement = ale_ate_decnumber
2й элемент: Start^='b', Length=6, Size=6, TypeOfElement = ale_ate_pair
    1й субэлемент: Start^='b', Length=1, Size=1, TypeOfElement = ale_ate_unknowntext
    2й субэлемент: Start^='''', Length=4, Size=2, TypeOfElement = ale_ate_textconst

Схематичный алгоритм работы парсера

Вся обработка текста осуществляется методом ProcessText класса TAleSimpleParser, вызываемым из ParseText.

function ProcessText(Text:PWideChar; Len:LongInt; var Res:TAleTextElement; PEnd:PWideChar; Flags:LongInt; UserParam:LongInt=0):PWideChar;

Метод возвращает указатель на символ до которого был обработан текст.
Параметры:
Text - указатель на первый символ обрабатываемого текста.
Len - длина обрабатываемого текста.
Res - переменная, в которой будет храниться указатель на первый элемент двунаправленного списка с выделенными из текста фрагментами.
PEnd - строка, при достижении которой следует завершить обработку. В случае PEnd=nil обрабатываются все Len символов строки.
Flags - параметры обработки текста.
UserParam - параметр для передачи пользовательских данных (см. ниже)

Метод может вызываться рекурсивно. Рекурсивный вызов используется при обработке скобок. В этом случае Text указывает на следующий после '(' символ, Len содержит оставшуюся длину текста. В параметре Res передается локальная переменная, которая будет указывать на начало списка фрагментов внутри скобок. PEnd будет указывать на строку ')', это позволит не вычислять закрывающую скобку в тексте и провести обработку за один проход. В параметре Flags будет установлен бит ale_opt_TextEndRequired = $00000020, означающий что обработка должна завершиться только при достижении PEnd, в противном случае будет сгенерирована ошибка, означающая отсутствие закрывающей скобки. После обработки скобок процесс продолжится с позиции, которую вернул метод. При вызове ProcessText внутри ParseText в качестве параметра PEnd передается значение свойства TextEnd.

Внутри метода ProcessText обработка происходит следующим образом.

N = оставшаяся длина обрабатываемого текста
P = указатель на обрабатываемый символ

Пока N>0 происходит цикл обработки текущего символа P^.
Первым действием в цикле является вызов виртуального метода SkipElement.

function SkipElement(var Pos:PWideChar; Len:LongInt; var Items:TAleTextElement; PEnd:PWideChar; Flags:LongInt; UserParam:LongInt):LongInt;virtual;

По-умолчанию данный метод не выполняет никаких действий. Предназначен для расширения функциональности парсера в классах-потомках.
Возвращаемое значение: отрицательное - произошла ошибка обработки текста, завершить обработку. Ноль - нормальное завершение метода, продолжение обработки текста. Положительное - нормальное завершение метода, обработку текста остановить (таким способом можно реализовать альтернативу свойству TextEnd).

Параметры:
Pos - текущая позиция обрабатываемого текста. Позиция должна быть изменена если метод распознал и выделил из текста фрагмент на следующий после фрагмента символ. Если позиция не была изменена - это означает, что метод ничего не обработал.
Len - оставшаяся длина текста.
Items - указатель на первый элемент списка, в котором хранятся выделенные фрагменты. В общем случае, если метод выделил из текста какой-то фрагмент или несколько фрагментов за раз, то он должен добавить их в конец этого списка.
PEnd - строка-окончание обрабатываемого текста. Обработка окончания текста реализуется в методе ProcessText, но в каких-то случаях информация о строке-окончании может быть необходима.
Flags - параметры обработки текста. Метод SkipElement вызывается в цикле два раза - в самом начале и самом конце. При вызове в начале в переменной Flags установлен бит ale_opt_FirstCall.
UserParam - внутри метода SkipElement можно вызывать метод ProcessText, который в свою очередь будет неявно рекурсивно вызывать метод SkipElement. Чтобы передать какие-то значения в этих вызовах можно использовать UserParam. Для служебных целей UserParam нигде не используется.

В зависимости от значения, возвращенного SkipElement, обработка продолжается или останавливается. Если метод SkipElement изменил позицию (P), то корректируется оставшаяся длина текста N и управление переходит на начало цикла.

В противном случае, определяется, чему соответствует символ P^, соответствующий фрагмент выделяется и добавляется в список. После чего управление передается в начало цикла. Также пропускаются пробелы и комментарии.

Если соответствий для символа P^ обнаружено не было, вызывается метод SkipElement. Это второй вызов SkipElement в цикле. Если SkipElement не изменил текущую позицию, то P увеличивается на 1 символ, N уменьшается на 1 и управление преходит в начало цикла.

(the project is discontinued, further development for .net, c#)