Графематика:
программный интерфейс
3.
Графематические дескрипторы
6. Пример
работы с интерфейсом
"Мама
мыла раму", здесь ПРД1 будет стоять на Мама,
а не на открывающей кавычке.
Графематический анализ (ГрафАн) - это
программа начального анализа естественного текста
(ЕТ), представленного в виде цепочки текстовых
знаков (ASCII символов),
вырабатывающая информацию,
необходимую для дальнейшей обработки Морфологическим и
Синтаксическим процессорами. В задачу графематического
анализа входят:
1) Разделение входного текста на слова, разделители и т.д.
2) Сборка
слов, написанных в разрядку;
3) Выделение
устойчивых оборотов, не имеющих словоизменительных вариантов;
4) Выделение
дат в цифровых форматах;
5) Выделение
ФИО (фамилия, имя, отчество), когда имя и отчество написаны инициалами;
6) Выделение
электронных адресов;
7) Выделение
предложений из входного текста;
8) Выделение
абзацев, заголовков, примечаний.
Графематический
анализ оформлен как СОM-объект, для которого создано описание на языке idl.
На вход графематике подается файл плайн-текста в Windows-кодировке. На выходе графематика
строит таблицу, состоящую из двух столбцов. В первом столбце стоит
некоторый кусок входного текста (выделенный по правилам, о которых мы скажем
ниже), во втором столбце стоят графематические
дескрипторы, характеризующие этот кусок текста. Например, из текста
«Иван спал» будет построена таблица из трех строк
Кусок входного
текста
Графематические дескрипторы
Иван |
ЛЕ Бб ПРД1 |
_ |
РЗД ПРБ |
спал |
ЛЕ бб ПРД2 |
В первый столбец всегда помещается часть входного
текста, если эта часть не является последовательностью из мягких разделителей
(пробел, табуляция, возврат каретки). В последнем случае используются другие
символы, номера которых включены в описание на языке idl.
Опишем теперь все графематические
дескрипторы. У каждого графематического дескриптора
есть два названия: кириллическое и латинское. Первое используется во втором
столбце графематической таблице, второе внутри
программы. Сначала приведем главные дескрипторы, один из которых обязательно
должен присутствовать на каждой строке графематической
таблицы.
Кир. Название |
Лат. Название |
Объяснение |
Примеры |
ЛЕ |
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’
и т.д.). После этого процедура заменяет все латинские буквы на
соответствующие кириллические. Таким образом, исправляются некоторые ошибки
ввода текста.
Графематический анализ работает
как 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 |
Этот пример начинается с инициализации и загрузки графематического модуля. Программа выставляет две опции.
Первая опция требует, чтобы граф. модуль записал в файл “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;
}
Этот пример показывает, как нужно запускать графематику от строкового буфера, а не от файла:
#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;
}
Этот алгоритм работает на графематике,
поскольку на результаты его работы опирается макросинтаксический
анализ. На вход алгоритма подается два числа StartPos
и EndPos, которые обозначают первую и последнюю
строки входного текста. На начале предложения алгоритм ставит помету ПРД1, на конце – ПРД2.
Программа ищет конец предложения (ПРД2),
а потом после него ищет начало предложения (ПРД1). Алгоритм основывается
на следующих постулатах:
1. Начало текста
совпадает с началом первого предложения, конец текста – с концом последнего.
2. Предложение
всегда начинается с большой буквы;
3. Предложение не
бывает больше одного абазаца.
4. Предложение не
может состоять только из знаков препинания.
Это означает, что некоторые знаки препинания, если они не
будут отнесены к предыдущему предложению, могут остаться вне предложений.
Например,
На самом деле, все дальнейшие анализаторы (постморфология, синтаксис) используют в своей работе только
помету ПРД2, что означает, что текст разбивается
полностью, все строки оказываются охваченными. Помета ПРД1
нужна лишь только для нужд графематики.
Определим вспомогательный примитив IsSentenceEndMark.
На вход подается номер строки. Функция возращает
истину, если эта строка содержит символ «?», «!», «.»
или многоточие
Определим вспомогательный примитив IsSentenceEndSeq.
На вход подается номер строки. Функция возращает
истину в двух следующих случаях:
Программа делает следующее:
Этот алгоритм работает избыточно, это означает, что он
иногда ставит пометы ПРД2 и ПРД1 по многу раз на одной
строке. Например, в конце абзаца ПРД2 может быть
поставлена по пп 3. 4. и 6.