PDA

Просмотр полной версии : Склонение ФИО по падежам



vga
20.11.2004, 16:17
Доброго времечка, господа!

Есть ли у кого готовый алгоритм склонения по падежам
Фамилии Имени Отчества?

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

xpymep
17.12.2004, 10:55
Я не вижу тут трудностей...например, такой код:
ПыЗы:
Borland C++ Builder


#define davatelniy 0x10
char* Padezh(char* slovo,char* okon,int pad)
{
AnsiString str = new AnsiString (slovo);
str.Delete(str.Pos(okon),str.Length);
switch (pad)
{
....
case 0x10:
{
str+= "у";
break;
}
default:
throw Exception("Unknown падеж :) ");
....
}

return str.c_str();
}

Всего и делов...А вот как программа работает:


char sur_name [15] = "Панасенко";
char name[15] = "Олександр"
char po_batkovi[15] = "Олександрович";
char a[15],b[15],c[15];
strcat (a,padezh(sur_name));
strcat (b,padezh(name));
strcat (c,padezh(po_batkovi));
ShowMessage(AnsiString(a)+"\t"+AnsiString(b)+"\t"+AnsiString(c));

Программа выведет следующее:
Панасенку Олександру Олександровичу
Всего и делов...

DeeJayC
17.12.2004, 11:07
Особенно хорошо должны при таком раскладе склоняться фамилии, например
Сухих или Ли. Вот здорово-то?
Сухиху и Лиу.

vga
17.12.2004, 11:21
Спасибо. Трудности как раз не в алгоритме, а найти уже готовые исходики.

Недавно нашел исходики, написанные для 1C и на скороую руку набрасал функции, эмулирующие 1C.
Просьба не ругать, времени не было разбирать исходный код на 1C, который был просто ужасен для чтения;(



/////////////////////////////////////////////////////////////////////////////
#define C(c, f, s ) ((c) ? (f):(s) )

//------------------------------------------------------------------
class CPadej
{
public:
CPadej::CPadej() { Clear(); }
virtual CPadej::~CPadej() { Clear();}
void Clear();
CString& Left(const char* sz0, int nCount);
CString& Trim(const char* sz0);
CString& Right(const char* sz0, int nCount);
CString& Mid(const char* sz0, int iFirst);
CString& Mid(const char* sz0, int iFirst, int nCount);
CString& TrimRight(const char* sz0);
CString& ToLow(const char* sz0);
CString& ToUpper(const char* sz0);
CString& Replace(const char* sz, const char* sz0, const char* sz1);
CString& New(const char* sz);
CString& PadejWord(CString& z1, int z2 = 2, const char* z3="*", int z4=0);
const char* Padej(CString& z1, int z2=2, int z3=3, CString z4="123", int z5=1);

protected:
std::vector <CString*> _vStr;

};


//------------------------------------------------------------------
inline int Find(const char* sz0, const char* sz1)
{
char* p = strstr(sz0, sz1);
if (!p)
return 0;
else
return p - sz0 + 1;
}

//------------------------------------------------------------------
void CPadej::Clear()
{
for (size_t i=0; i< _vStr.size(); i++)
{
delete _vStr[i];
_vStr[i] = NULL;
}
_vStr.clear();
}

//------------------------------------------------------------------
CString& CPadej::Left(const char* sz0, int nCount)
{
CString str = sz0;
if (nCount > str.GetLength())
nCount = str.GetLength();
else if (nCount < 0)
nCount = 0;

CString* p = new CString();
*p = str.Left(nCount);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::Trim(const char* sz0)
{
CString str = sz0;
CString* p = new CString();
*p = str.Trim();
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::Right(const char* sz0, int nCount)
{
CString str = sz0;
if (nCount > str.GetLength())
nCount = str.GetLength();
else if (nCount < 0)
nCount = 0;

CString* p = new CString();
*p = str.Right(nCount);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::Mid(const char* sz0, int iFirst, int nCount)
{
CString str = sz0;
if ((iFirst-1) > str.GetLength())
iFirst = str.GetLength()+1;
else if ((iFirst-1) < 0)
iFirst = 1;

CString* p = new CString();
*p = str.Mid(iFirst-1, nCount);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::Mid(const char* sz0, int iFirst)
{
CString str = sz0;
if ((iFirst-1) > str.GetLength())
iFirst = str.GetLength()+1;
else if ((iFirst-1) < 0)
iFirst = 1;

CString* p = new CString();
*p = str.Mid(iFirst-1);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::TrimRight(const char* sz0)
{
CString str = sz0;
CString* p = new CString();
*p = str.TrimRight();
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::ToLow(const char* sz0)
{
CString* p = new CString();
*p = sz0;
_strlwr((char*)(LPCTSTR)*p);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::ToUpper(const char* sz0)
{
CString* p = new CString();
*p = sz0;
_strupr((char*)(LPCTSTR)*p);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::Replace(const char* sz, const char* sz0, const char* sz1)
{
CString* p = new CString();
*p = sz;
p->Replace(sz0, sz1);
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//------------------------------------------------------------------
CString& CPadej::New(const char* sz)
{
CString* p = new CString();
*p = sz;
_vStr.push_back(p);
return *_vStr[ _vStr.size()-1 ];
}

//
// SuperJur.Narod.Ru
// Функция для склонения одного слова!!!
// z1 - само слово
// z2 - номер падежа
// z3 - пол
// z4 - 1-склонять как фамилию, 2-имя, 3-отчество
CString& CPadej::PadejWord(CString& z1, int z2, const char* z3, int z4)
{
int z5, za, zb, zc, zd, ze;
const char *z6, *z7, *z8, *z9;
CString str, str1, str2, str3, str4, str5, str6, str7;
CString str10, zf;
z5=Find(z1,"-");
str10 = "-";
z6=C(z5==0,"",str10 + PadejWord(Mid(z1,z5+1,strlen(z1)-z5+1),z2,z3,z4));
z1=ToLow(C(z5==0,z1,Left(z1,z5-1)));
z7=Right(z1,3);
z8=Right(z7,2);
z9=Right(z8,1);
z5=strlen(z1);
za=Find("ая ия ел ок яц ий па да ца ша ба та га ка",z8);
zb=Find("аеёийоуэюяжнгхкчшщ",Left(z7,1));
zc= z2 < 0 ? -z2:z2;
zd=C(za==4,5,Find("айяь",z9));
zd=C((zc==1)||(*z9=='.')||((z4=2)&&(Find(C(*z3=='ч',"оиеу","оиеубвгджзклмнпрстфхцчшщъ"),z9)>0))||
((z4==1)&&(Find("мия мяэ лия кия жая лея",z7)>0)),9,C((zd==4) && (*z3=='ч'),2,C(z4==1,
C(Find("оеиую",z9)+Find("их ых аа еа ёа иа оа уа ыа эа юа яа",z8)>0,9,
C(*z3!='ч',C(za==1,7,C(*z9=='а',C(za>18,1,6),9)),C(((Find("ой ый",z8)>0)&&
(z5>4)&&( strcmp(Right(z1,4),"опой") != 0 )) || ((zb>10) && (za==16)),8,zd))),zd)));
ze=Find("лец вей бей дец пец мец нец рец вец аец иец ыец бер",z7);

zf=C((zd==8)&&(zc!=5),C((zb>15) || ( Find("жий ний",z7)>0),"е","о"),
C( strcmp(z1,"лев") == 0,"ьв",C((Find("аеёийоуэюя",Mid(z1,z5-3 ,1))==0)&&
((zb>11) || (zb=0)) && (ze!=45),"",C(za==7,"л",C(za==10,"к",C(za==13,"йц",
C(ze==0,"",C(ze<12,C(ze==1,"ьц","ь"),C(ze<37,"ц",C(ze<49,"йц","р"))))))))));

str1 = Mid("оыые",Find("внч",z9)+1,1);
str2 = C(Find("гжкхш",Left(z8,1))>0,"и","ы");
str3 = "а у а "+ str1 +"ме "+ str2 + " е у ойе я ю я ем" + C(za==16,"и","е")+
" и е ю ейе и и ь ьюи и и ю ейи ойойу ойойойойуюойойгомуго";
str4 = Left(z1,z5-C((zd>6) || (*zf!= '\x0'),2, C(zd>0,1,0)));
str5 = str3+C((*zf=='е') || (za==16) || ((zb>12) && (zb<16)),"и","ы")+"мм";
str6 = Mid(str5,10*zd+2*zc-3,2);
str7 = TrimRight(str6);

zf=C((zd==9) || ((z4==3) && (*z3=='ы')),z1,str4+zf+str7);

str10 = C(z4>0,ToUpper(Left(zf,1))+C((z2<0)&&(z4>1),".",Mid(zf,2)),zf);
return New( C( !z1.GetLength(),"",str10+z6));
}

//__________________________________________________ ___________________________
// z1 - фамилия имя отчество например Железняков Юрий Юрьевич
// z2 - Падеж ( по умолчанию = 2 - родительный)
// 2 - родительный ( нет кого? ) Железнякова Юрия Юрьевича
// 3 - дательный ( кому? ) Железнякову Юрию Юрьевичу
// 4 - винительный ( вижу кого? ) Железнякова Юрия Юрьевича
// 5 - творительный ( кем? ) Железняковым Юрием Юрьевичем
// 6 - предложный ( о ком? ) Железнякове Юрии Юрьевиче
// Если задать Z2 меньше 0, то на выходе получим от -1=Железняков Ю. Ю. до -6=Железнякове Ю. Ю.
// z3 - параметр Пол может не указываться, но при наличии фамилий с
// инициалами точное определение пола невозможно, поэтому предлагается задавать пол этим
// параметром 1 - мужской 2 - женский
// ---------------------------------------------------------------------------------------
// Бибик Галушка Цой Николайчик Наталия Петровна Герценберг Кривошей Капица-Метелица
// Если Падеж(Фио ,1 ,3), то на выходе получим Фамилия Имя Отчество и т.д.
// Если Падеж(Фио ,1 ,3,"1" ), то Фамилия
// Если Падеж(Фио ,1 ,3,"2" ), то Имя
// Если Падеж(Фио ,1 ,3,"3" ), то Отчество
// Если Падеж(Фио, 1 ,3,"12" ), то Фамилия Имя
// Если Падеж(Фио, 1 ,3,"23" ), то Имя Отчество
// Если Падеж(Фио,-1 ,3,"231" ),то И. О. Фамилия
// Если Падеж(Фио,-1 ,3,"23" ), то И. О.
// 10-11-2003 3-20
// z5 - recursion level
const char* CPadej::Padej(CString& z1, int z2, int z3, CString z4, int z5)
{
// Функция Падеж(z1,z2=2,z3=3,Знач z4="123",z5=1) Экспорт
Clear();
CString str, str1, str2, str3, str4, str5;
CString str10, str11, str15;
str10 = z4;
str11 = "ча";
str15.Format("%d", z5 );

str1 = Trim(Replace(Mid(z1,Find(z1+" "," ")+1),".",". "));
str2 = Left(z1,Find(z1+" "," ")-1);
str3 = Mid( str11+ToLow(Right(z1,1)),z3,1);
str4 = PadejWord( str2,z2, str3,z5)+" ";
str5 = Replace(str10,str15,str4);
return New( C(z5<4,Padej(str1,z2,z3, str5,z5+1),z4));
}


Пример использования
Cpadej oPadej;

oPadej.Padej( "Иванов Петр Алексеевич",2, 1));
oPadej.Padej( "Аликперова Нигель Амбросьевна",2, 2));

xpymep
17.12.2004, 11:46
DeeJayC, тогда надо сделать маленькую мелочь в функцие :


#define davatelniy 0x10
char* Padezh(char* slovo,char* okon,int pad)
{
AnsiString str = new AnsiString (slovo);
if (strcmp(okon,"")==0)
{
str.Delete(str.Pos(okon),str.Length);
switch (pad)
{
....
case 0x10:
{
str+= "у";
break;
}
default:
throw Exception("Unknown падеж :) ");
....
}
}

return str.c_str();
}

Естественно, что моя функция не идеальная...
vga, посмотрел на твой алгоритм...тихий ужас :) . Я бы сдох если бы писал это все сам! Но, как говорят, искусство треьует жертв ! :)

vga
17.12.2004, 12:10
Это не мой алгоритм, а некого программиста 1C.
Я бы тоже сдох, если бы пытался на с++ перевести нормальным образом ;-)

xpymep
17.12.2004, 12:23
Я себе представляю такого программиста.... Большая голова, как телевизор,очки 1м на 1м...сидит за стареньким пеньком, а еще лучше за K6 или что-то вроде... Объем винта не более 2Гб,на которых меститься только вин95 и 1С. Наверняка днями не отходит от компа :)

vga
17.12.2004, 12:43
Я предполагаю, что такой код очень сильно ускоряет быстродействие 1C скриптов ;-)

AiK
17.12.2004, 13:10
Хорош флеймить!
Кстати, фамилия Панасенко по правилам русского языка не склоняется.

xpymep
17.12.2004, 18:14
AiK, Гы...точно... Это мой,кста, одноклассник. И моя училка как раз вчера говорила, что в украинском языке его фамилия склоняется, а в русском нет :). Провтыкал ... надо будет подучиться :)

Naeel Maqsudov
28.12.2004, 12:58
фамилия Панасенко по правилам русского языка не склоняется

А еще по правилам русского языка такие фамили как Вальтер, Фишер, Шлюндт, Ширвиндти т.п. склоняются , либо не склоняются в зависимоти от рода. И вообще окончания фамилий и имен в коре зависят от рода...

Наример: Вальтер Анне Петровне, но Вальтеру Евгению Самсоновичу. Кстати, равно как и, например, армянские фамилии: Петросян Земфире Рафиковне (Рафик-кызы), но Петросяну Араму Саргисовичу (Саргис-оглы)

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

В русском все понятно, я надеюсь ;).
В болгарском:
Петров Велеслав Иванов -> Петрову Велеславу Иванову (Иванов - это отчество, произностится с ударением на букве А)
Петрова Велеслава Иванова -> Петровой Велеславе Ивановой

В татарском отчества чаще образуют по правилам русского языка.

В армянском - когда как... пример был выше. (оглы - буквально сын, кызы - дочь)

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

Бета-реализацию на паскале смогу закинуть сегоня вечером.

В любом случае этот алгоритм нельзя будет использовать для автоматического пакетного формирования документоов. Он никогда не будет надежным на 100%. Этот алгоритм можно использовать для ввода данных. Юзер заполняет поля Ф, И и О, а аналогичные поля для родительного падежа заполняются автомамтически, с возможностью последующей корректировки.

Naeel Maqsudov
29.12.2004, 01:53
Вот код из реально внедренного проекта



unit grammar;

interface

type
TGender=(masculine,feminine,genunknown);
TSetOfChar=set of char;

const
consonant=['б','в','г','д','ж','з','к','л' ,'м','н','п','р','с','т','ф','х','ц','ч' ,'ш','щ'];
vowel=['а','е','ё','и','о','у','ы','э','ю ','я'];

function NameInGenitiveCase(Gender:TGender;Name:string):str ing;
function SurnameInGenitiveCase(Gender:TGender;Surname:strin g):string;
function PatronymicInGenitiveCase(Gender:TGender;Patronymic :string):string;

implementation


function ReplaceEnding(var Word:string; OldEnding:TSetOfChar; NewEnding:string; RemoveOld:boolean):boolean; overload;
var
wl:integer;
begin
result:=false;
wl:=length(Word);
if (wl>0) and (Word[wl] in OldEnding) then begin
if RemoveOld then SetLength(Word,wl-1);
Word:=Word+NewEnding;
result:=true;
end;
end;
function ReplaceEnding(var Word:string; OldEnding:Char; NewEnding:string; RemoveOld:boolean):boolean; overload;
var
wl:integer;
begin
result:=false;
wl:=length(Word);
if (wl>0) and (Word[wl]=OldEnding) then begin
if RemoveOld then SetLength(Word,wl-1);
Word:=Word+NewEnding;
result:=true;
end;
end;
function ReplaceEnding(var Word:string; OldEnding, NewEnding:string):boolean; overload;
var
wl,el,i:integer;
begin
result:=false;
wl:=length(Word);
el:=length(OldEnding);
if wl>=el then begin
for i:=el downto 1 do if OldEnding[i]<>Word[wl-el+i] then exit;
SetLength(Word,wl-el);
Word:=Word+NewEnding;
result:=true;
end;
end;

function NameInGenitiveCase(Gender:TGender;Name:string):str ing;
begin
result:=Name;
case Gender of
masculine:begin
if ReplaceEnding(Result,'ь','я',true) or
ReplaceEnding(Result,'й','я',true) or
ReplaceEnding(Result,'ья','ьи') or
ReplaceEnding(Result,vowel,'и',true) or
ReplaceEnding(Result,consonant,'а',false) then;
end;
feminine:begin
if ReplaceEnding(Result,'га','ги') or
ReplaceEnding(Result,'ша','ши') or
ReplaceEnding(Result,'ча','чи') or
ReplaceEnding(Result,'ца','ци') or
ReplaceEnding(Result,'ка','ки') or
ReplaceEnding(Result,'а','ы',true) or
ReplaceEnding(Result,'я','и',true) then;
end;
end;
end;
function SurnameInGenitiveCase(Gender:TGender;Surname:strin g):string;
begin
result:=Surname;
case Gender of
masculine:begin
if ReplaceEnding(Result,'ий','ого') or
ReplaceEnding(Result,'ый','ого') or
ReplaceEnding(Result,'ай','ая') or
ReplaceEnding(Result,consonant,'а',false) then;
end;
feminine:begin
if ReplaceEnding(Result,'ва','вой') or
ReplaceEnding(Result,'на','ной') or
ReplaceEnding(Result,'ая','ой') then;
end;
end;
end;
function PatronymicInGenitiveCase(Gender:TGender;Patronymic :string):string;
begin
result:=Patronymic;
case Gender of
masculine:begin
if ReplaceEnding(Result,'ч','а',false) or
ReplaceEnding(Result,consonant,'а',false) then;
end;
feminine:begin
if ReplaceEnding(Result,'на','ны') or
ReplaceEnding(Result,'ва','вой') then;
end;
end;
end;

end.



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

А вот и определение рода:



function GenCaseFIO(const s:string):string;
var
f,i,o,t:string; gen:TGender;
begin
f:=ExtractWord(1,S,StdWordDelims);
i:=ExtractWord(2,S,StdWordDelims);
o:=trim(ExtractWord(3,S,StdWordDelims)+' '+ExtractWord(4,S,StdWordDelims));
if length(o)>2 then begin
if (o[length(o)] in ['ч','в']) or ((o[length(o)] in ['ы','э']) and (o[length(o)-1]='л')) then gen:=masculine
else if (o[length(o)] = 'а') or ((o[length(o)] in ['ы','э']) and (o[length(o)-1]='з')) then gen:=feminine
else gen:=genunknown;
end else if length(f)>2 then begin
t:=f[length(f)]+f[length(f)];
if (t='ов') or (t='ий') then gen:=masculine
else if (t='ва') or (t='ий') then gen:=masculine
else gen:=genunknown;
end else
gen:=genunknown;
result:=SurnameInGenitiveCase(gen,f)+' '+NameInGenitiveCase(gen,i)+' '+PatronymicInGenitiveCase(gen,o);
end;