Графематика: программный интерфейс

 

Графематика: программный интерфейс.. 1

1. Общее описание. 1

2. Входные и выходные данные. 1

3. Графематические дескрипторы... 1

5. Программный интерфейс.. 4

6. Пример работы с интерфейсом... 5

Пример 1. 5

Пример 2. 6

7. Деление на предложения.. 7

"Мама мыла раму", здесь ПРД1 будет стоять на Мама, а  не на открывающей кавычке.7

 

1. Общее описание

 

    Графематический анализ (ГрафАн) -  это  программа     начального  анализа естественного текста (ЕТ),  представленного в    виде цепочки текстовых знаков  (ASCII  символов),  вырабатывающая       информацию,  необходимую для дальнейшей обработки Морфологическим   и Синтаксическим процессорами. В задачу графематического анализа входят:
   1) Разделение входного текста на слова, разделители и т.д.

2)   Сборка слов, написанных в разрядку;

3)   Выделение устойчивых оборотов, не имеющих словоизменительных вариантов;

4)   Выделение дат в цифровых форматах;

5)   Выделение ФИО (фамилия, имя, отчество), когда имя и отчество написаны инициалами;

6)   Выделение электронных адресов;

7)   Выделение предложений из входного текста;

8)   Выделение абзацев, заголовков, примечаний.

   Графематический анализ оформлен как СОM-объект, для которого создано описание на языке idl.

 

 

2. Входные и выходные данные

 

На вход графематике подается файл плайн-текста в Windows-кодировке. На выходе графематика строит таблицу, состоящую из двух столбцов. В первом столбце  стоит некоторый кусок входного текста (выделенный по правилам, о которых мы скажем ниже), во втором столбце стоят графематические дескрипторы, характеризующие этот кусок текста. Например,  из текста
«Иван спал» будет построена таблица из трех строк

      Кусок входного текста                            Графематические дескрипторы

Иван

ЛЕ Бб ПРД1

_

РЗД ПРБ

спал

ЛЕ бб ПРД2


    В первый  столбец всегда помещается часть входного текста, если эта часть не является последовательностью из мягких разделителей (пробел, табуляция, возврат каретки). В последнем случае используются другие символы, номера которых включены в описание на языке idl.

 

 

3. Графематические дескрипторы

 

  Опишем теперь  все графематические дескрипторы. У каждого графематического дескриптора есть два названия: кириллическое и латинское. Первое используется во втором столбце графематической таблице, второе внутри программы. Сначала приведем главные дескрипторы, один из которых обязательно должен присутствовать на каждой строке графематической таблицы.

Кир. Название

Лат. Название

Объяснение

Примеры

ЛЕ

OLE

русская лексема, присваивается последовательностям, состоящим из кириллицы

Иван

ИЛЕ

OILE

иностранная лексема, присваивается  последовательностям из латиницы

John

РЗД

ODel

разделитель.

‘*', '=', '_'

ЗПР

ОPun

знак препинания, присваивается последовательностям, состоящим из одинаковых знаков препинания

‘.’, '[', ']', '(', ')', '-', ':', ';'

ЦК

ODg

цифровой комплекс, присваивается последовательностям, состоящим из цифр

1234

ЦБК

ODgCh

цифро-буквенный комплекс, присваивается последовательностям, состоящим из цифр и букв

34h

???

OUnk

сложный узел, присваивается последовательностям, не обладающим вышеперечисленными признаками

 


 Разновидности дескриптора РЗД:

ПРБ

OSpc

строка пробелов или табуляций

 

КСТ   

OEOLN

признак конца строки

 

ПАР

OParagraph

символ параграфа

 

ПС

ONil

нулевой символ

 

 

Разновидности дескриптора ЗПР:

ОТК

OOpn

открывающая скобка

'{', '[', '('

ЗАК

OCls

закрывающая скобка

'}', ']', ')'

ДЕФ

OHyp

дефис

-

 

Разновидности дескриптора ЗПР и РЗД:

ДЗПР

OLongDelimSeq

последовательность одинаковых символов, длина которой больше 20

 

МНЖ

Oplu

последовательность одинаковых символов, длина которой больше 1

 

 

Разновидности дескриптора ЛЕ и ИЛЕ:

бб

OLw

признак того, что все символы лексемы - малые

мама

Бб

OUpLw

признак того, что первый символ лексемы - большой;

Мама

ББ

OUp

признак того, что все символы лексемы - большие

МАМА

 

 

Теперь опишем дескрипторы, которые появляются на строке в зависимости от контекста, т.е. они вычисляются не только из текущей строки, но и из номера текущей строки и строк, которые находятся выше и ниже вычисляемой.

 

Контекстные дескрипторы:

НАЧ

BTxt

ставится на начале текста (входного файла), т.е. всегда стоит на нулевой строке таблице. Причем, важно сказать, что нулевая строка таблицы используется как служебная ( содержимое первого столбца нулевой строки не входит во входной текст)

 

КФР

OEOP

ставится на конце фразыонцом фразы считается только ‘;‘.

 

ПРД1

OSent1

начало предложения

 

ПРД2

OSent2

конец предложения

 

ИМ?

ONam

признак того, что лексема, возможно, является частью

имени собственного. Присваивается лексеме, начинающейся с большой

буквы и не имеющей перед собой символа конца предложения.

 

ПП

OBullet

ставится на начале пункта перечисления

 

АБЗ

OPar

ставится на начале абзаца

 

ОБ1

OOb1

ставится на начале оборота

типа «во взаимодействии с»

ОБ2

OOb2

ставится на конце оборота

 

ФИ1

OFIO1

ставится на начале ФИО

типа «Иванов И.И.»

ФИ2

OFIO2

ставится на конце ФИО

 

ДЕ1

OSHyp1

ставится на слове, после которого идет дефис и конец строки, после которых идет другое слово

 

ДЕ2

OSHyp2

ставится на слове, до которого идет конец строки  и дефис, до которых идет другое слово

 

ДТ1

ODate1

ставится на начале даты

 

ДТ2

ODate2

ставится на конце даты

 

РЕ1

ORef1

было использовано для офиц. текстов

 

РЕ2

ORef2

было использовано для офиц. текстов

 

ЧПТ1

OFloat1

начало числа с плавающей точкой

111.111

ЧПТ2

OFloat2

конец числа с плавающей точкой

 

ЭА1

OElectAddr1

начало электронного адреса

ftp.com.com

ЭА2

OElectAddr2

конец электронного адреса

 

АБ1

OАbbr1

начало  сокращения

и т.п.

АБ2

OАbbr2

конец сокращение

 

 

 

 

 

 

 

 

Кроме этого, используются дескрипторы, относящиеся к макросинтаксическому анализу (анализу расположения абзацев, заголовков). В макросинтакксическом анализе[1] абзацы, заголовки и т.д. называются условно предложениями (УП). Макросинтаксические дескрипторы  ставятся на конце УП в 2зависимости от типа УП.

УП?

CS_Undef

ставится на конце УП, тип которого не определен

 

УП

CS_Simple

ставится на конце простого УП

 

Заг

CS_Heading

ставится на конце заголовка

 

прим

CS_Explan

ставится на конце УП, заключенного в скобки

 

УПввод

CS_Parent

ставится на конце УП, заканчивающегося на двоеточие

 

Док

CS_Doc

ставится на нулевой строке графематической таблицы

 

Все объяснения того, как работает макросинтаксический анализ можно найти в [1].

 

Параметр ForceToRus включает/запрещает запуск процедуры кириллизации.  Эта процедура находит слова, которые  содержат смесь латинских и кириллических букв, причем все латинские   буквы могут быть переведена  в соответствующие кириллические (латинское ‘о’ в кириллическое ‘o’ и т.д.). После этого процедура заменяет все латинские буквы на соответствующие кириллические. Таким образом, исправляются некоторые ошибки ввода текста.

 

 

5. Программный интерфейс

 

 

Графематический анализ работает как COM-объект (graphan.dll), описание которого содержится в файле graphan.idl[2].  Вначале файла graphan.idl определены графематические дескрипторы (константы типа UINT), затем  идет

описание интерфейса графематического анализа IGraphmatFile. IGraphmatFile  обладает следующими методами:

0)  LoadDicts () – загружает все словари в память. Путь к словарям лежит в файле настройки.

1)  FreeDicts– выгружает все словари из памяти. FreeDictsавтоматически выполняется  при деструкторе.

2)  LoadFileIntoGraphan([in] BSTR CommandLine) - создать графематическую таблицу для входного файла.
   CommandLine - здесь содержится имя входного файла, для которого нужно создать таблицу. Например, «test.txt».

3)  LoadStringIntoGraphan([in] BSTR Buffer) - создать графематическую таблицу для входного буфера.

4)  BOOLIsTableLoaded  - истина, если графематическая таблица была построена (с помощью LoadStringили LoadFile), но функция FreeTableеще  не была вызвана.

5)  GetLineCount([retval, out]UINT* Count) - выдает число строк в графематической таблице;

6)  HasDescr([in] UINT LineNo, [in]UINT D, [retval, out] BOOL* result) - возращает TRUE, если строка с номером LineToсодержит дескриптор D, в противном случае возращает FALSE.

7)  GetWord ([in] UINTLineNo, [retval, out] BSTR* s) - возращает кусок входного текста, стоящий в первой колонке строки с номером LineNo. Все символы, номера которых меньше 32 (non-printablesymbols), превращаются в пробелы кроме символов с номерами:
    а) 9 – табуляция (остается с таким же номером)
    б) 13 – перенос каретки (‘\r’) заменяется пустой строкой.

8)  GetLine([in] UINT LineNo, [retval, out] BSTR* result) – возращает графематическу строчку в формате файла *.gra. Сначала идет кусок входного текста в конв. виде, потом длина, смещения в файле, реальная длина в файле, графематические дескриторы.

9)  GetOborotNo([in] UINT LineNo, [retval, out] WORD* result) –
возращает номер оборота, который начинается с этой строки. Эту функцию можно только вызывать, если на этой строке присутствует дескриптор OOb1 (начало оборота).

10) FreeTable() - убрать из памяти графематическую таблицу. FreeTableавтоматически выполняется  при деструкторе.

7)      GetDescriptorStr([in] UINTDescriptorNo, [retval, out] BSTR* result) –  выдает  текстовое представление для графематического дескриптора.

8)      GetRubiconTypeStr([in]UINTRubiconTypeNo, [retval, out] BSTR* result) – выдает текстовое представление типа рубикона (смотри ниже) .

9)      GetUnitOffset ([in] LONGLineNo, [retval, out] LONG* FilePos) – выдает смещение в графематическом буфере , начиная с  которого была получена строка с номером LineNo;

10)  GetUnitLength ([in] LONGLineNo, [retval, out] BYTE* result ) – выдает длину входного текста, с которого была получена строка с номеромLineNo;

 

 

Булевские опции настройки графематического модуля. 

 

Название

Объяснение

Значение по умолчанию

GraOutputFile

Имя файла для выходной графематической таблицы, по умолчанию равен “” (файл не создается)  

Пустая строка (файл не создается)

XmlMacSynOutputFile

Имя файла для выходной макросинтаксической структуры

Пустая строка (файл не создается)

bForceToRus

нужно ли запукать процедуру кириллизации, которая  переводит латинские буквы  в кириллицу, если эти буквы графематически эквивалентны

False

bMacSynHierarchy

нужно ли внутри макросинтаксического анализа запускать процедуру, которая построит иерархию УП.

false

bSubdueWrongMacroSynUnitToMainRoot

Ecли true, то макросинтаксический анализ все спорные УП подчинятся главному УП (нулевой строке таблицы), в противном случае он подчиняет их предыдущем по тексту.

true

Language

Язык графематики:

Russian=1, English=2,

German=3

1

bWriteTextBuffer

нужно ли записывать содержимое графематического буфера в файл с расширением tds.

false

bSaveHtmlFileToTdsFile

нужно ли записывать содержимое графематического буфера в файл с расширением tds, если входной файл имел расширение html

false

bEmptyLineIsSentenceDelim

считать ли пустую строку концом предложения

false

bUseParagraphTagToDivide

нужно ли использовать конечный таг </p> для разделения на  абзацы

false

bUseIndention

нужно ли использовать  красные строки для разделения на абзацы

false

bOnlyNonContextDescriptors

Нужно ли запускать только деление текста на слова (без вилочных дескрипторов)

false

 

 

     

 

6. Пример работы с интерфейсом

 

Пример 1

 

Этот пример начинается  с инициализации и загрузки графематического модуля. Программа выставляет две опции. Первая  опция  требует, чтобы  граф. модуль записал в файл “out.gra графематическое представление входного текста. Вторая опция  требует, чтобы модуль записал макросинтаксическое представление текста в файл “out.xml“.  Далее программ читает файл, имя которого задано в параметрах программы. Потом она  печатает количество найденных графематических слов во входном файле.  Потом печатает на экран  все слова файла,  которые  помечены дескриптором  ИЛЕ(слова, написанные латиницей).

 

/*

 This example loads the input file into graphematics and prints

 all found English words.

*/

 

#include <stdio.h>

#include "string.h"

#include <string>

#import "../../bin/graphan.tlb"

 

 

 

 

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

{    

      if (argc != 2)

      {

            printf ("TestComGraphan, Test for Dialing Graphematical Analysis(COM)(www.aot.ru)\n");

            printf ("Usage: TestComGraphan <file>\n");

            printf ("where file is an text or html file\n");

            return 1;

      };

      // init COM

      CoInitialize(NULL);

 

      GRAPHANLib::IGraphmatFilePtr piGraphan;

     

      // creating COM-object

      HRESULT hr = piGraphan.CreateInstance(__uuidof(GRAPHANLib::GraphmatFile));

      if (FAILED (hr))

      {

            printf ("Graphan is not registered");

            CoUninitialize();

            return 1;

      };

 

      // loading dictionaries

      hr = piGraphan->LoadDicts();

 

      piGraphan->Language = 1;

 

      // write the graphematical table to .gra file

      piGraphan->GraOutputFile = "out.gra";

 

      // write the graphematical macro syntax structure .xml file

      piGraphan->XmlMacSynOutputFile = "out.xml";

 

      //  loading file

      printf ("Loading file %s\n", argv[1]);

      piGraphan->LoadFileToGraphan(argv[1]);

     

 

      // get the number of rows in the graphematical table

      size_t Count = piGraphan->GetLineCount();

      printf ("Found Units %i\n", Count);

 

      // print all words which are written in English ABC

for (size_t i =0; i< Count; i++)

            if (piGraphan->HasDescr(i,GRAPHANLib::OLLE) == TRUE)

         {

               std::string s = (const char*) piGraphan->GetWord (i);  // get the token

               printf ("%s\n", s.c_str());

 

         };

      piGraphan = 0;

      CoUninitialize();

      return 0;

}

 

 

 

Пример 2

 

Этот пример показывает, как нужно запускать графематику от строкового буфера, а не от файла:

#include "stdio.h"

#include "string.h"

#include "string"

#import "../../bin/graphan.tlb"//  импортируембиблиотеку

 

// String Example

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

{

  try

  {

      // инициализируем библиотеку COM

      CoInitialize(NULL);

      // инициализируем переменную класса IGraphmatFilePtr

      GRAPHANLib::IGraphmatFilePtrpiGraphan;

      HRESULThr = piGraphan.CreateInstance(__uuidof(GRAPHANLib::GraphmatFile));

      if (FAILED (hr))

      {

            printf ("Graphan is not registered");

            CoUninitialize();

            return 1;

      };

 

      piGraphan.CreateInstance(__uuidof(GRAPHANLib::GraphmatFile));

 

      piGraphan->LoadDicts();

 

      // загружаем строчку в графематику

      piGraphan->LoadStringToGraphan("мама мыла раму");

 

      _bstr_t t = piGraphan->GetWord(3);

 

      assert (t == _bstr_t("раму"));

  }

  catch(...)

  {

            return -1;

  }

  CoUninitialize();

  return 1;

}

 

 

7. Деление на предложения

 

Этот алгоритм работает на графематике, поскольку на результаты его работы  опирается макросинтаксический анализ. На вход алгоритма подается два числа StartPos и EndPos, которые обозначают  первую и последнюю строки входного текста. На начале предложения алгоритм ставит помету ПРД1, на конце – ПРД2.

Программа ищет конец предложения (ПРД2), а потом после него ищет начало предложения (ПРД1). Алгоритм  основывается на следующих постулатах:

1.      Начало текста совпадает с началом первого предложения, конец текста – с концом последнего.

2.      Предложение всегда начинается с большой буквы;

3.      Предложение не бывает больше одного абазаца.

4.      Предложение не может состоять только из знаков препинания.

Это означает, что некоторые знаки препинания, если они не будут отнесены к предыдущему предложению, могут остаться вне предложений. Например,

"Мама мыла раму", здесь ПРД1 будет стоять на Мама, а  не на открывающей кавычке.

  На самом деле, все дальнейшие анализаторы (постморфология, синтаксис) используют в своей работе только помету ПРД2, что означает, что текст разбивается полностью, все строки оказываются охваченными. Помета ПРД1 нужна лишь только для нужд графематики.

Определим вспомогательный примитив IsSentenceEndMark. На вход подается  номер строки. Функция возращает истину, если эта строка содержит символ «?», «!», «.» или многоточие

Определим вспомогательный примитив IsSentenceEndSeq. На вход подается  номер строки. Функция возращает истину в двух следующих случаях:

  1. Если  для этой строки верна функция IsSentenceEndMark; и контактно справа нет закрывающей кавычки (если предложение закавычено, закрывающая кавычка входит в это предложение);
  2. Если строка является закрывающей кавычкой, а а контактно слева стоит строка, для которой верно IsSentenceEndMark.

 

Программа делает следующее:

  1. Проходит все знаки препинания  в начале текста и ставит первому слову помету ПРД1.
  2. Пусть i – текущая строка между StartPos и EndPos.
  3. Если на строке i стоит помета начала абазаца,  тогда нужно пройти назад все пробелы и длинные разделители  (ДЗПР) и дойти до конца предыдущего абзаца. Если в конце абзаца (до первого слова) стоит строка, которая удовлетворяет IsSentenceEndSeq, тогда нужно поставить ПРД2 этой строке, иначе  нужно поставить ПРД2 на конец предыдущего абзаца.
  4. Если на строке i стоит помета макросинтаксическая помета УП, тогда  нужно сделать то же самое, что и в пункте 3, только надо учесть, что помета УП ставится на конце абзаца, а не начале (как в пункте 3).
  5. Если до начала текущего предложений стояла открывающая скобка, и текущая строка указывает на слово до соотв. закрывающей скобки, тогда нужно поставить ПРД2 на закрывающую кавычку, а текущую строку сместить на ближайшее  после закрывающей кавычки слово.
  6. Если текущая строка удовлетворяет функции IsSentenceEndSeq и не входит в графематические группы (ФИ1-ФИ2, ДТ1-ДТ2 и т.д.) непоследним словом, тогда проходим все знаки препинания от текущей строки. Проверяем, что знак препинания, который заканчивает предложения, не должен стоять в самом начале строки. Далее ищем первое слово от текущей строки и считаем его началом нового предложения. 

 

Этот алгоритм работает избыточно, это означает, что он иногда ставит пометы ПРД2 и ПРД1 по многу раз на одной строке. Например, в конце абзаца ПРД2  может быть поставлена по пп 3. 4. и 6.