Работа с индексами Clipper'а
Посылаю кое-что из своих наработок:
NtxRO - Модуль чтения clipper-овских индексов. Удобен для доступа к данным Clipper приложений. Предусмотрено, что программа может работать с индексом даже если родное приложение производит изменение в индексе NtxAdd - Средство формирования своих Clipper подобных индексов. Индексы НЕ БУДУТ ЧИТАТЬСЯ Clipper-приложениями (кое-что не заполнил в заголовке, очень было лениво, да и торопился)До модуля удаления из Индекса ключей все никак не дойдут руки. Меня очень интересуют аналогичные разработки для индексов Fox-а Кстати реализация индексов Clipper наиболее близка из всех к тому, что описано у Вирта в "Алгоритмах и структурах данных"
Я понимаю, что мне могут возразить, что есть дескать Apollo и т.п., но я считаю что предлагаемая реализация наиболее удобна ТАК КАК ИНДЕКСЫ НЕ ПРИВЯЗАНЫ К НАБОРУ ДАННЫХ (а лишь поставляют физические номера записей) это позволяет делать кое-какие фокусы (например перед индексацией преобразовать значение какой нибудь функцией типа описанной ниже, не включать индексы для пустых ключевых значений в разреженных таблицах, строить индексы контекстного поиска, добавляя по нескольку значений на одну запись, строить статистики эффективности поиска различных ключевых значений (для фамилии Иванов например статистика будет очень плохой) и т.п.)
В файле Eurst.inc функция нормализации фамилий (типа Soundex) В основном это ориентировано на фамилии нашего (Татарстанского) региона
Файл Eurst.inc
var vrSynonm:integer=0;
vrPhFine:integer=0;
vrUrFine:integer=0;
vrStrSyn:integer=0;
function fContxt(const s:ShortString):ShortString; var i:integer; r:ShortString;
c,c1:char;
begin r:=''; c1:=chr(0);for i:=1 to length(s) do
begin
c:=s[i];
if c='Ё' then c:='Е';
if not (c in ['А'..'Я','A'..'Z','0'..'9','.']) then c:=' ';
if (c=c1)and not (c1 in ['0'..'9']) then continue;
c1:=c;
if (c1 in ['А'..'Я'])and(c='-')and(i<length(s))and(s[i+1]=' ') then
begin
c1:=' ';
continue;
end;
r:=r+c;
end;
procedure _Cut(var s:ShortString;p:ShortString);begin if Pos(p,s)=length(s)-length(p)+1 then
s:=Copy(s,1,length(s)-length(p));
end;function _PhFace(const ss:ShortString):ShortString; var r:ShortString; i:integer;
s:ShortString;
begin r:='';
s:=ANSIUpperCase(ss);
if length(s)<2 then
begin
Result:=s;
exit;
end;
_Cut(s,'ЕВИЧ');
_Cut(s,'ОВИЧ');
_Cut(s,'ЕВНА');
_Cut(s,'ОВНА');
for i:=1 to length(s) do
begin
if length(r)>12 then break;
if not(s[i] in ['А'..'Я','Ё','A'..'Z']) then break;
if (s[i]='Й')and((i=length(s))
or(not (s[i+1] in ['А'..'Я','Ё','A'..'Z']))) then continue;
{ЕЯ-ИЯ Андриянов}
if s[i]='Е' then
if (i>length(s))and(s[i+1]='Я') then s[i]:='И';
{Ж,З-С Ахметжанов}
if s[i]in ['Ж','З'] then s[i]:='С';
{АЯ-АЙ Шаяхметов}
if s[i]='Я' then
if (i>1)and(s[i-1]='А') then s[i]:='Й';
{Ы-И Васылович}
if s[i] in ['Ы','Й'] then s[i]:='И';
{АГЕ-АЕ Зулкагетович, Шагиахметович, Шадиахметович}
if s[i] in ['Г','Д'] then
if (i>1) and (i<length(s)) then
if (s[i-1]='А')and(s[i+1] in ['Е','И']) then continue;
{О-А Арефьев, Родионов}
if s[i]='О' then s[i]:='А';
{ИЕ-Е Галиев}
if s[i]='И' then
if (i>length(s))and(s[i+1]='Е') then continue;
{Ё-Е Ковалёв}
if s[i]='Ё' then s[i]:='Е';
{Э-И Эльдар}
if s[i]='Э' then s[i]:='И';
{*ЯЕ-*ЕЕ Черняев}
{(И|С)Я*-(И|С)А* Гатиятуллин}
if s[i]='Я' then
if (i>1)and(i<length(s)) then
begin
if s[i+1]='Е' then s[i]:='Е';
if s[i-1] in ['И','С'] then s[i]:='А';
end;
{(А|И|Е|У)Д-(А|И|Е|У)Т Мурад}
if s[i]='Д' then
if (i>1)and(s[i-1] in ['А','И','Е','У']) then s[i]:='Т';
{Х|К-Г Фархат}
if s[i] in ['Х','К'] then s[i]:='Г';
if s[i] in ['Ь','Ъ'] then continue;
{БАР-БР Мубракзянов}
if s[i]='А' then
if (i>1)and(i>length(s)) then
if (s[i-1]='Б')and(s[i+1]='Р') then continue;
{ИХО-ИТО Вагихович}
if s[i] in ['Х','Ф','П'] then
if (i>1)and(i<length(s)) then
if (s[i-1]='И')and(s[i+1]='О') then s[i]:='Т';
{Ф-В Рафкат}
if s[i]='Ф' then s[i]:='В';
{ИВ-АВ Ривкат см. Ф}
if s[i]='И' then
if (i<length(s))and(s[i+1]='В') then s[i]:='А';
{АГЕ-АЕ Зулкагетович, Сагитович, Сабитович}
if s[i] in ['Г','Б'] then
if (i>1)and(i<length(s)) then
if (s[i-1]='А')and(s[i+1] in ['Е','И']) then continue;
{АУТ-АТ Зияутдинович см. ИЯ}
if s[i]='У' then
if (i>1)and(i<length(s)) then
if (s[i-1]='А')and(s[i+1]='Т') then continue;
{АБ-АП Габдельнурович}
if s[i]='Б' then
if (i>1)and(s[i-1]='A') then s[i]:='П';
{ФАИ-ФИ Рафаилович}
if s[i]='А' then
if (i>1)and(i<length(s)) then
if (s[i-1]='Ф')and(s[i+1]='И') then continue;
{ГАБД-АБД}
if s[i]='Г' then
if (i=1)and(length(s)>3)and(s[i+1]='А')and(s[i+2]='Б')and(s[i+3]='Д') then continue;
{РЕН-РИН Ренат}
if s[i]='Е' then
if (i>1)and(i<length(s)) then
if (s[i-1]='Р')and(s[i+1]='Н') then s[i]:='И';
{ГАФ-ГФ Ягофар}
if s[i]='А' then
if (i>1)and(i<length(s)) then
if (s[i-1]='Г')and(s[i+1]='Ф') then continue;
{??-? Зинатуллин}
if (i>1)and(s[i]=s[i-1]) then continue;
r:=r+s[i];
end;
Result:=r;
end;
|
Файл NtxAdd.pas
unit NtxAdd; interface uses classes,SysUtils,NtxRO; type TNtxAdd=class(TNtxRO)
protected
function Changed:boolean; override;
function Add(var s:ShortString;var rn:integer;var nxt:integer):boolean;
procedure NewRoot(s:ShortString;rn:integer;nxt:integer); virtual;
function GetFreePtr(p:PBuf):Word;
public
constructor Create(nm:ShortString;ks:Word);
constructor Open(nm:ShortString);
procedure Insert(key:ShortString;rn:integer);
end;
implementation function TNtxAdd.GetFreePtr(p:PBuf):Word; var i,j:integer; r:Word;
fl:boolean;
beginr:=(max+2)*2;
for i:=1 to max+1 do
begin fl:=True;
for j:=1 to GetCount(p)+1 do
if GetCount(PBuf(@(p^[j*2])))=r then fl:=False;
if fl then
begin
Result:=r;
exit;
end;
r:=r+isz;
end;
Result:=0;
end;function TNtxAdd.Add(var s:ShortString;var rn:integer;var nxt:integer):boolean; var p:PBuf; w,fr:Word;
i:integer;
tmp:integer;
beginwith tr do
begin
p:=GetPage(h,(TTraceRec(Items[Count-1])).pg);
if GetCount(p)
begin
fr:=GetFreePtr(p);
if fr=0 then
begin
Self.Error:=True;
Result:=True;
exit;
end;
w:=GetCount(p)+1;
p^[0]:=w and $FF; p^[1]:=(w and $FF00)shr 8;
w:=(TTraceRec(Items[Count-1])).cn;
for i:=GetCount(p)+1 downto w+1 do
begin
p^[2*i] :=p^[2*i-2];
p^[2*i+1]:=p^[2*i-1];
end;
p^[2*w] := fr and $FF;
p^[2*w+1]:=(fr and $FF00)shr 8;
for i:=0 to length(s)-1 do p^[fr+8+i]:=ord(s[i+1]);
for i:=0 to 3 do
begin
p^[fr+i]:=nxt mod $100;
nxt:=nxt div $100;
end;
for i:=0 to 3 do
begin
p^[fr+i+4]:=rn mod $100;
rn:=rn div $100;
end;
FileSeek(h,(TTraceRec(Items[Count-1])).pg,0);
FileWrite(h,p^,1024);
Result:=True;
end
else
begin
fr:=GetCount(p)+1;
fr:=GetCount(PBuf(@(p^[fr*2])));
w:=(TTraceRec(Items[Count-1])).cn;
for i:=GetCount(p)+1 downto w+1 do
begin
p^[2*i] :=p^[2*i-2];
p^[2*i+1]:=p^[2*i-1];
end;
p^[2*w] := fr and $FF;
p^[2*w+1]:=(fr and $FF00)shr 8;
for i:=0 to length(s)-1 do p^[fr+8+i]:=ord(s[i+1]);
for i:=0 to 3 do
begin
p^[fr+i+4]:=rn mod $100;
rn:=rn div $100;
end; tmp:=0;
for i:=3 downto 0 do tmp:=$100*tmp+p^[fr+i];
for i:=0 to 3 do
begin
p^[fr+i]:=nxt mod $100;
nxt:=nxt div $100;
end;
w:=hlf;
p^[0]:=w and $FF; p^[1]:=(w and $FF00)shr 8;
fr:=GetCount(PBuf(@(p^[(hlf+1)*2])));
s:=''; rn:=0;
for i:=0 to ksz-1 do
begin
s:=s+chr(p^[fr+8+i]);
p^[fr+8+i]:=0;
end;
for i:=3 downto 0 do
begin
rn:=$100*rn+p^[fr+i+4];
p^[fr+i+4]:=0;
end;
nxt:=FileSeek(h,0,2);
FileWrite(h,p^,1024);
for i:=1 to hlf do
begin
p^[2*i] :=p^[2*(i+hlf+1)];
p^[2*i+1]:=p^[2*(i+hlf+1)+1];
end;
for i:=0 to 3 do
begin
p^[fr+i]:=tmp mod $100;
tmp:=tmp div $100;
end;
FileSeek(h,(TTraceRec(Items[Count-1])).pg,0);
FileWrite(h,p^,1024);
Result:=False;
end;
end;
end;procedure TNtxAdd.NewRoot(s:ShortString;rn:integer;nxt:integer); var p:PBuf; i,fr:integer;
beginp:=GetPage(h,0);
for i:=0 to 1023 do p^[i]:=0;
fr:=(max+2)*2;
p^[0]:=1;
p^[2]:=fr and $FF; p^[3]:=(fr and $FF00)shr 8;
for i:=0 to length(s)-1 do p^[fr+8+i]:=ord(s[i+1]);
for i:=0 to 3 do
begin
p^[fr+i]:=nxt mod $100;
nxt:=nxt div $100;
end;
for i:=0 to 3 do
begin
p^[fr+i+4]:=rn mod $100;
rn:=rn div $100;
end;
fr:=fr+isz;
p^[4]:=fr and $FF; p^[5]:=(fr and $FF00)shr 8;
nxt:=GetRoot;
for i:=0 to 3 do
begin
p^[fr+i]:=nxt mod $100;
nxt:=nxt div $100;
end;
nxt:=FileSeek(h,0,2);
FileWrite(h,p^,1024);
FileSeek(h,4,0);
FileWrite(h,nxt,sizeof(integer));
end;procedure TNtxAdd.Insert(key:ShortString;rn:integer); var nxt:integer; i:integer;
begin nxt:=0;
if DosFl then key:=WinToDos(key);
if length(key)>ksz then key:=Copy(key,1,ksz);
for i:=1 to ksz-length(key) do key:=key+' ';
Clear;
Load(GetRoot);
Seek(key,False);
while True do
begin
if Add(key,rn,nxt) then break;
if tr.Count=1 then
begin
NewRoot(key,rn,nxt);
break;
end;
Pop;
end;
end;constructor TNtxAdd.Create(nm:ShortString;ks:Word); var p:PBuf; i:integer;
beginError:=False;
DeleteFile(nm);
h:=FileCreate(nm);
if h>0 then
begin
p:=GetPage(h,0);
for i:=0 to 1023 do p^[i]:=0;
p^[14]:=ks and $FF; p^[15]:=(ks and $FF00)shr 8; ks:=ks+8;
p^[12]:=ks and $FF; p^[13]:=(ks and $FF00)shr 8; i:=(1020-ks)div(2+ks); i:=i div 2;
p^[20]:=i and $FF; p^[21]:=(i and $FF00)shr 8; i:=i*2; max:=i;
p^[18]:=i and $FF; p^[19]:=(i and $FF00)shr 8; i:=1024;
p^[4 ]:=i and $FF; p^[5 ]:=(i and $FF00)shr 8;
FileWrite(h,p^,1024);
for i:=0 to 1023 do p^[i]:=0; i:=(max+2)*2;
p^[2 ]:=i and $FF; p^[3 ]:=(i and $FF00)shr 8;
FileWrite(h,p^,1024);
end else Error:=True;
FileClose(h);
FreeHandle(h);
Open(nm);
end;constructor TNtxAdd.Open(nm:ShortString); begin Error:=False;
h:=FileOpen(nm,fmOpenReadWrite or fmShareExclusive);
if h>0 then
begin
FileSeek(h,12,0);
FileRead(h,isz,2);
FileSeek(h,14,0);
FileRead(h,ksz,2);
FileSeek(h,18,0);
FileRead(h,max,2);
FileSeek(h,20,0);
FileRead(h,hlf,2);
DosFl:=True;
tr:=TList.Create;
end else Error:=True;
end;function TNtxAdd.Changed:boolean; begin Result:=(csize=0);
csize:=-1;
end;end. |
Файл NtxRO.pas
unit NtxRO; interface uses Classes; type TBuf=array[0..1023]of Byte; PBuf=^TBuf;
TTraceRec=class
public
pg:integer;
cn:SmallInt;
constructor Create(p:integer;c:SmallInt);
end;
TNtxRO=class
protected
fs:string[10];
empty:integer;
csize:integer;
rc:integer; {Текущий номер записи}
tr:TList; {Стек загруженных страниц}
h:integer; {Дескриптор файла}
isz:Word; {Размер элемента}
ksz:Word; {Размер ключа}
max:Word; {Максимальное кол-во элементов}
hlf:Word; {Половина страницы}
function GetRoot:integer; {Указатель на корень}
function GetEmpty:integer; {Пустая страница}
function GetSize:integer; {Возвращает размер файла}
function GetCount(p:PBuf):Word; {Число элементов на странице}
function Changed:boolean; virtual;
procedure Clear;
function Load(n:integer):PBuf;
function Pop:PBuf;
function Seek(const s:ShortString;fl:boolean):boolean;
function Skip:PBuf;
function GetItem(p:PBuf):PBuf;
function GetLink(p:PBuf):integer;
public
Error:boolean;
DosFl:boolean;
constructor Open(nm:ShortString);
destructor Destroy; override;
function Find(const s:ShortString):boolean;
function GetString(p:PBuf;c:SmallInt):ShortString;
function GetRecN(p:PBuf):integer;
function Next:PBuf;
end;
function GetPage(h,fs:integer):PBuf; procedure FreeHandle(h:integer); function DosToWin(const ss:ShortString):ShortString; function WinToDos(const ss:ShortString):ShortString; implementation uses Windows, SysUtils; const MaxPgs=5; var Buf:array[1..1024*MaxPgs]of char; Cache:array[1..MaxPgs]of record
Handle:integer; {0-страница свободна}
Offset:integer; { смещение в файле}
Countr:integer; { счетчик использования}
Length:SmallInt;
end;
function TNtxRO.Next:PBuf; var cr:integer; p:PBuf;
beginif h<=0 then
begin
Result:=nil;
exit;
end;
while Changed do
begin
cr:=rc;
Find(fs);
while cr>0 do
begin
p:=Skip;
if GetRecN(p)=cr then break;
end;
end;
Result:=Skip;
end;function TNtxRO.Skip:PBuf; var cnt:boolean; p,r:PBuf;
n:integer;
begin r:=nil;cnt:=True;
with tr do
begin
p:=GetPage(h,(TTraceRec(Items[Count-1])).pg);
while cnt do
begin cnt:=False;
if (TTraceRec(Items[Count-1])).cn>GetCount(p)+1 then
begin
if Count<=1 then
begin
Result:=nil;
exit;
end;
p:=Pop;
end
else
while True do
begin
r:=GetItem(p);
n:=GetLink(r);
if n=0 then break;
p:=Load(n);
end;
if (TTraceRec(Items[Count-1])).cn>=GetCount(p)+1 then cnt:=True
else r:=GetItem(p);
Inc((TTraceRec(Items[Count-1])).cn);
end;
end;
if r<>nil then
begin
rc:=GetRecN(r);
fs:=GetString(r,length(fs));
end;
Result:=r;
end;function TNtxRO.GetItem(p:PBuf):PBuf; var r:PBuf; begin with TTraceRec(tr.items[tr.Count-1]) do
r:=PBuf(@(p^[cn*2]));
r:=PBuf(@(p^[GetCount(r)]));
Result:=r;
end;function TNtxRO.GetString(p:PBuf;c:SmallInt):ShortString; var i:integer; r:ShortString;
begin r:='';if c=0 then c:=ksz;
for i:=0 to c-1 do
r:=r+chr(p^[8+i]);
if DosFl then r:=DosToWin(r);
Result:=r;
end;function TNtxRO.GetLink(p:PBuf):integer; var i,r:integer; begin r:=0; for i:=3 downto 0 do
r:=r*256+p^[i];
Result:=r;
end;function TNtxRO.GetRecN(p:PBuf):integer; var i,r:integer; begin r:=0; for i:=3 downto 0 do
r:=r*256+p^[i+4];
Result:=r;
end;function TNtxRO.GetCount(p:PBuf):Word; begin Result:=p^[1]*256+p^[0];
end;function TNtxRO.Seek(const s:ShortString;fl:boolean):boolean; var r:boolean; p,q:PBuf;
nx:integer;
begin r:=False;with TTraceRec(tr.items[tr.Count-1]) do
begin
p:=GetPage(h,pg);
while cn<=GetCount(p)+1 do
begin
q:=GetItem(p);
if (cn>GetCount(p))or(s<GetString(q,length(s))) or
(fl and (s=GetString(q,length(s)))) then
begin
nx:=GetLink(q);
if nx<>0 then
begin
Load(nx);
r:=Seek(s,fl);
end;
Result:=r or (s=GetString(q,length(s)));
exit;
end;
Inc(cn);
end;
end;
Result:=False;
end;function TNtxRO.Find(const s:ShortString):boolean; var r:boolean; begin if h<=0 then
begin
Result:=False;
exit;
end;
rc:=0;
csize:=0;
r:=False;
while Changed do
begin
Clear;
Load(GetRoot);
if length(s)>10 then fs:=Copy(s,1,10)
else fs:=s;
R:=Seek(s,True);
end;
Result:=r;
end;function TNtxRO.Load(N:integer):PBuf; var it:TTraceRec; r:PBuf;
begin r:=nil;if h>0 then
begin
with tr do
begin
it:=TTraceRec.Create(N,1);
Add(it);
end;
r:=GetPage(h,N);
end;
Result:=r;
end;procedure TNtxRO.Clear; var it:TTraceRec; begin while tr.Count>0 do
begin
it:=TTraceRec(tr.Items[0]);
tr.Delete(0);
it.Free;
end;
end;function TNtxRO.Pop:PBuf; var r:PBuf; it:TTraceRec;
begin r:=nil;with tr do
if Count>1 then
begin
it:=TTraceRec(Items[Count-1]);
Delete(Count-1);
it.Free;
it:=TTraceRec(Items[Count-1]);
r:=GetPage(h,it.pg)
end;
Result:=r;
end;function TNtxRO.Changed:boolean; var i:integer; r:boolean;
begin r:=False;if h>0 then
begin
i:=GetEmpty;
if i<>empty then r:=True;
empty:=i;
i:=GetSize;
if i<>csize then r:=True;
csize:=i;
end;
Result:=r;
end;constructor TNtxRO.Open(nm:ShortString); begin Error:=False;
h:=FileOpen(nm,fmOpenRead or fmShareDenyNone);
if h>0 then
begin
fs:='';
FileSeek(h,12,0);
FileRead(h,isz,2);
FileSeek(h,14,0);
FileRead(h,ksz,2);
FileSeek(h,18,0);
FileRead(h,max,2);
FileSeek(h,20,0);
FileRead(h,hlf,2);
empty:=-1;
csize:=-1;
DosFl:=True;
tr:=TList.Create;
end else Error:=True;
end;destructor TNtxRO.Destroy; begin if h>0 then
begin
FileClose(h);
Clear;
tr.Free;
FreeHandle(h);
end;
inherited Destroy;
end;function TNtxRO.GetRoot:integer; var r:integer; begin r:=-1; if h>0 then
begin
FileSeek(h,4,0);
FileRead(h,r,4);
end;
Result:=r;
end;function TNtxRO.GetEmpty:integer; var r:integer; begin r:=-1; if h>0 then
begin
FileSeek(h,8,0);
FileRead(h,r,4);
end;
Result:=r;
end;function TNtxRO.GetSize:integer; var r:integer; begin r:=0; if h>0 then r:=FileSeek(h,0,2);
Result:=r;
end;constructor TTraceRec.Create(p:integer;c:SmallInt); begin pg:=p;
cn:=c;
end;function GetPage(h,fs:integer):PBuf; {Протестировать отдельно} var i,j,mn:integer; q:PBuf;
beginmn:=10000; j:=0;
for i:=1 to MaxPgs do
if (Cache[i].Handle=h) and
(Cache[i].Offset=fs) then
begin
j:=i;
if Cache[i].Countr<10000 then
Inc(Cache[i].Countr);
end;
if j=0 then
begin
for i:=1 to MaxPgs do
if Cache[i].Handle=0 then j:=i;
if j=0 then
for i:=1 to MaxPgs do
if Cache[i].Countr<=mn then
begin
mn:=Cache[i].Countr;
j:=i;
end;
Cache[j].Countr:=0;
mn:=0;
end;
q:=PBuf(@(Buf[(j-1)*1024+1]));
if mn=0 then
begin
FileSeek(h,fs,0);
Cache[j].Length:=FileRead(h,q^,1024);
end;
Cache[j].Handle:=h;
Cache[j].Offset:=fs;
Result:=q;
end;procedure FreeHandle(h:integer); var i:integer; begin for i:=1 to MaxPgs do
if Cache[i].Handle=h then
Cache[i].Handle:=0;
end;function DosToWin(const ss:ShortString):ShortString; var r:ShortString; i:integer;
begin r:='';for i:=1 to length(ss) do
if ss[i] in [chr($80)..chr($9F)] then r:=r+chr(ord(ss[i])-$80+$C0)
else if ss[i] in [chr($A0)..chr($AF)] then r:=r+chr(ord(ss[i])-$A0+$C0)
else if ss[i] in [chr($E0)..chr($EF)] then r:=r+chr(ord(ss[i])-$E0+$D0)
else if ss[i] in [chr($61)..chr($7A)] then r:=r+chr(ord(ss[i])-$61+$41)
else if ss[i] in [chr($F0)..chr($F1)] then r:=r+chr($C5)
else r:=r+ss[i];
Result:=r;
end;function WinToDos(const ss:ShortString):ShortString; var r:ShortString; i:integer;
begin r:='';for i:=1 to length(ss) do
if ss[i] in [chr($C0)..chr($DF)] then r:=r+chr(ord(ss[i])-$C0+$80)
else if ss[i] in [chr($E0)..chr($FF)] then r:=r+chr(ord(ss[i])-$E0+$80)
else if ss[i] in [chr($F0)..chr($FF)] then r:=r+chr(ord(ss[i])-$F0+$90)
else if ss[i] in [chr($61)..chr($7A)] then r:=r+chr(ord(ss[i])-$61+$41)
else if ss[i] in [chr($D5), chr($C5)] then r:=r+chr($F0)
else r:=r+ss[i];
Result:=r;
end;end. |
[000975]