Работа с индексами Clipper'а

=== Работа с индексами 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;
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;
begin
 r := (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;
begin
 with tr do
 begin
 p := GetPage(h, (TTraceRec(Items[Count - 1])).pg);
 if GetCount(p) then
 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;
begin
 p := 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;
begin
 Error := 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;
begin
 if 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;
begin
 mn := 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.
Source: Взято из Советов по Delphi от Валентина Озерова
ID: 02975
Category:Базы данных