WAYFARER писал(а):Alexander писал(а):Разработчику надо написать вначале, чтобы он её освободил под GNU/*GPL и только потом использовать.
Они исходники при покупке отдают.
Добавлено спустя 6 часов 14 минут 11 секунд:Решил поделиться своей библиотекой.
https://github.com/WAYFARER87/NameCasePasВ свое время накидал на коленке и использовал 1 раз для обработки большого набора данных. Особо не тестировалась, но работает. Библиотека использует набор правил от небезызвестной ruby библиотеки Petrovich (
https://github.com/petrovich/petrovich-rules)
Лицензия MIT, полностью кроссплатформенная. В репозитарии так же пример использования. Кроме склонения ФИО по падежам умеет определять пол.
Если кому то нужно, то я нормально оформлю репозитарий и создам страничку на форуме.
Юрия не правильно склоняет
- Код: Выделить всё
Юрийя
Юрийю
Юрийя
Юрийем
Юрийе
Добавлено спустя 11 часов 2 минуты 17 секунд:Еще и разные файлы в основном коде и в папке с примером
Добавлено спустя 3 часа 31 минуту 14 секунд:Короче есть пара ошибок и, что самое главное, применение к utf8 строкам функции length как будто она должна вернуть количество символов в строке, а она возвращает количество байт, естественно.
Для корректной работы, нужно переписать либо с utf8Length (и будет зависеть библиотека от LCL_Base) либо конвертировать все в UTF16 и держать в уме, что каждый символ в 4 байта, либо написать свою реализацию UTF8Lenght.
Я подправил, так чтобы оно работало, НО логику надо менять, тк. поиск в суффиксах идет последовательно, то, например для имени Юрий - подбирает суффикс "й", вместо "ий", и по этому не верно склоняет в предложном падеже.
код поправленной библиотеки:
- Код: Выделить всё
{The MIT License (MIT)
Copyright (c) 2024 Anton Lindeberg
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}
unit NameCasePas;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, fpjson, jsonparser, StrUtils, Dialogs;
type
{ TNameCase }
TNameCase = class
private
middlename, firstname, lastname, gender: string;
rules: TJSONData;
function inflect(Name: string; aCase: integer; ruleType: string): string;
function findInRules(Name: string; aCase: integer; ruleType: string): string;
function applyRule(Mods, Name: string; aCase: integer): string;
public
constructor Create;
function GetFirstName(AFirstName: string; ACase: integer): string;
function GetLastName(ALastName: string; ACase: integer): string;
function GetMiddleName(AMiddleName: string; ACase: integer): string;
function GetGender:String;
end;
const
CASE_DATIVE = 0; //родительный
const
CASE_GENITIVE = 1; //дательный
const
CASE_ACCUSATIVE = 2; //винительный
const
CASE_INSTRUMENTAL = 3; //творительный
const
CASE_PREPOSITIONAL = 4; //предложный
implementation
uses jsonscanner;
{ TCaseName }
constructor TNameCase.Create;
var
JSONParser: TJSONParser;
begin
JSONParser := TJSONParser.Create(TFileStream.Create(ExtractFilePath(ParamStr(0)) +
'rules.js', fmOpenRead), [TJSONOption.joUTF8]);
rules := JSONParser.Parse;
end;
function TNameCase.GetFirstName(AFirstName: string; ACase: integer): string;
begin
{ $this->firstname = $firstname;
return $this->inflect($this->firstname,$case,__FUNCTION__); }
self.firstname := AFirstName;
Result := inflect(firstname, ACase, 'firstname');
end;
function TNameCase.GetLastName(ALastName: string; ACase: integer): string;
begin
self.lastname := ALastName;
Result := inflect(lastname, ACase, 'lastname');
end;
function TNameCase.GetMiddleName(AMiddleName: string; ACase: integer): string;
begin
self.middlename := AMiddleName;
Result := inflect(middlename, ACase, 'middlename');
end;
function TNameCase.GetGender: String;
begin
Result:=gender;
end;
function CountChar(const str: string; const chr: char): integer;
var
i: integer;
begin
Result := 0;
for i := 1 to Length(str) do
if str[i] = chr then
Inc(Result);
end;
{ private function inflect($name,$case,$type) {
//если двойное имя или фамилия или отчество
if(substr_count($name,'-') > 0) {
$names_arr = explode('-',$name);
$result = '';
foreach($names_arr as $arr_name) {
$result .= $this->findInRules($arr_name,$case,$type).'-';
}
return substr($result,0,strlen($result)-1);
} else {
return $this->findInRules($name,$case,$type);
}
}}
function TNameCase.inflect(Name: string; aCase: integer; ruleType: string): string;
var
nameArr: TStringList;
i: integer;
res: string;
begin
if CountChar(Name, '-') > 0 then
begin
nameArr := TStringList.Create;
try
nameArr.DelimitedText := Name;
res := '';
for i := 0 to nameArr.Count - 1 do
res := res + findInRules(nameArr[i], aCase, ruleType) + '-';
Result := Copy(res, 1, Length(res) - 1);
finally
nameArr.Free;
end;
end
else
Result := findInRules(Name, aCase, ruleType);
end;
{ foreach($this->rules[$type]->suffixes as $rule) {
foreach($rule->test as $last_char) {
$last_name_char = substr($name,strlen($name)-strlen($last_char),strlen($last_char));
if($last_char == $last_name_char) {
if($rule->mods[$case] == '.')
continue;
if($this->gender == 'androgynous' || $this->gender == null)
$this->gender = $rule->gender;
return $this->applyRule($rule->mods,$name,$case);
}
}
}}
function TNameCase.findInRules(Name: string; aCase: integer; ruleType: string): string;
var
i, j: integer;
suffixes: TJSONData;
last_char: TJSONData;
last_name_char, rule: string;
begin
suffixes := rules.FindPath(ruleType).FindPath('suffixes');
for i := 0 to suffixes.Count - 1 do
begin
//last_name_char = substr($name,strlen($name)-strlen($last_char),strlen($last_char));
last_char := suffixes.Items[i].FindPath('test');
for j := 0 to last_char.Count - 1 do
begin
last_name_char := Copy(Name, Length(Name) - Length(last_char.Items[j].Value) +
1, Length(last_char.Items[j].Value));
if (last_char.Items[j].Value = last_name_char) then
begin
if suffixes.Items[i].FindPath('mods').Items[aCase].Value = '.' then Continue;
gender := suffixes.Items[i].FindPath('gender').Value;
Result := applyRule(suffixes.Items[i].FindPath(
'mods').Items[aCase].Value, Name, aCase);
end;
end;
end;
end;
//function substr_count(const substr: string; Str: string): integer;
function substr_count(Str: string; const substr: string): integer; //!!!
begin
if (Length(substr) = 0) or (Length(Str) = 0) or (Pos(substr, Str) = 0) then
Result := 0
else
//Result := (Length(Str) - Length(StringReplace(Str, substr, '', [rfReplaceAll]))) div
// Length(substr); // тут окажемся только когда в суффиксе есть "-" и какая-то буква. Length(substr) всегда = 1, нет смысла на него делить
Result:= str.CountChar('-') * 2; //будет работать только для кириллицы, за каждый символ "-" мы должны удалиить из имени по 2 байта
end;
function TNameCase.applyRule(Mods, Name: string; aCase: integer): string;
var
res: string;
begin
res := Copy(Name, 1, Length(Name) - substr_count(Mods, '-'));
res := res + StringReplace(Mods, '-', '', [rfReplaceAll]);
Result := res;
end;
end.