Склонение ФИО по падежам

Ответить

Код подтверждения
Введите код в точности так, как вы его видите. Регистр символов не имеет значения.

BBCode ВКЛЮЧЁН
[img] ВКЛЮЧЁН
[url] ВКЛЮЧЁН
Смайлики ОТКЛЮЧЕНЫ

Обзор темы
   

Развернуть Обзор темы: Склонение ФИО по падежам

Naeel Maqsudov » 29 дек 2004, 01:53

Вот код из реально внедренного проекта

Код: Выделить всё

unit grammar;

interface

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

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

function NameInGenitiveCase(Gender:TGender;Name:string):string;
function SurnameInGenitiveCase(Gender:TGender;Surname:string):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):string;
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:string):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;

Naeel Maqsudov » 28 дек 2004, 12:58

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

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

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

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

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

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

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

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

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

xpymep » 17 дек 2004, 18:14

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

AiK » 17 дек 2004, 13:10

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

vga » 17 дек 2004, 12:43

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

xpymep » 17 дек 2004, 12:23

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

vga » 17 дек 2004, 12:10

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

xpymep » 17 дек 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 дек 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));

DeeJayC » 17 дек 2004, 11:07

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

Вернуться к началу