Вобщем выяснилось вот что. Inf-файлы windows, с которыми и предполагалось работать имеют немного другую структуру, чем Ini-файлы:
| Код | [секция] строка 1 строка 2 ;... строка n
|
т.е. строки парсятся уже потом (для получения параметров вида <ключ>=<значение> и т.д)...
далее. ф-ии Windows для работы с INI-файлами не позволяют читать параметры с переносами строк:
| Код | ; Этот параметр взят из реального файла ; и демонстрирует многострочные параметры (с переносами) 0x3,"Software\Microsoft\MediaPlayer\Battery\Presets\DrowningFlower","Palette",\ 09,00,09,00,09,01,0c,00,09,03,10,00,09,04,14,00,09,06,17,00,09,07,1b,00,09,\ 09,1f,00,09,0a,22,00,09,0c,26,00,09,0d,2a,00,08,0f,2e,00,08,10,31,00,08,12,\ ; тут тоже может быть комментарий 35,00,08,13,39,00,08,15,3c,00,08,16,40,00,08,18,44,00,08,19,48,00,08,1b,4b ; {вырезано}
|
Такие параметры так-же могут использоваться в REG-файлах
Поэтому я решил написать модуль для работы с Inf-файлами. (в нем только самые необходимые функции для чтения/записи строк, парсинг параметров пока во внимание не берется)
Сначала я пытался дописать существующий модуль для работы с Ini-файлами IniFiles32 (от Stephan Schneider), но найдя там более 5 грубых ошибок, решил все-таки писать с нуля...
вот что из этого получилось:
| Код | {---------------------------------------------- InfFiles.pas
Модуль для работы с Inf-файлами Windows
Алексей Куликов (C) 2008 г.
freeware -----------------------------------------------}
unit InfFiles;
interface
uses Classes, SysUtils;
type
TInfFile = class (TObject) private FFileName: string; FFileBuffer: TStringList; procedure LoadFromFile; function ParseComments(const S: string): string; function IsSection(const S: string): boolean; function GetSectionIndex(const Section: string): integer; function RemoveStr(const Section: string; Target: string): integer; public constructor Create(const FileName: string); destructor Destroy; procedure Save(); procedure SaveAs(const FileName: string); procedure ReadSections(Strings: TStrings); procedure ReadStrings(const Section: string; Strings: TStrings); procedure WriteString(const Section: string; const Ext: string); procedure WriteStrings(const Section: string; Strings: TStrings); procedure ReplaceString(const Section: string; const Old: string; const New:string); procedure ReplaceStrings(const Section: string; const Old: string; const New: TStrings); procedure DeleteString(const Section: string; Target: string); end;
implementation
constructor TInfFile.Create(const FileName: string); begin FFileName := FileName; FFileBuffer := TStringList.Create; if FileExists(FFileName) then LoadFromFile; end;
destructor TInfFile.Destroy; begin FFileBuffer.Free; Finalize(FFileName); end;
{ Сохраняет изменения в открытом файле } procedure TInfFile.Save; begin FFileBuffer.SaveToFile(FFileName); end;
{ Сохраняет изменения в назначенном файле } procedure TInfFile.SaveAs(const FileName: string); begin FFileBuffer.LoadFromFile(FileName); end;
procedure TInfFile.LoadFromFile; begin FFileBuffer.LoadFromFile(FFileName); end;
{ Функция парсит строку, удаляя комментарии } function TInfFile.ParseComments(const S: string): string; var i, len: integer; tmp: string; begin len := length(S); i := 1; while (i < len+1) and (S[i] <> ';') do begin tmp := tmp + S[i]; Inc(i); end; result := tmp; end;
{ Функция возвращает true, если S яв-ся секцией } function TInfFile.IsSection(const S: string): boolean; var tmp: string; begin Result := false; if S <> '' then begin tmp := Trim(s); if (tmp[1] = '[') and (tmp[Length(tmp)] = ']') then Result := true; end; end;
{ Функция возвращает инедекс секции причем только первый (если секция разбросана по файлу) } function TInfFile.GetSectionIndex(const Section: string): integer; begin Result := FFileBuffer.IndexOf('['+Section+']'); end;
{ Функция получает список всех секций файла } procedure TInfFile.ReadSections(Strings: TStrings); var i: integer; Section: string; bufcount: integer; begin Strings.BeginUpdate; try Strings.Clear; bufcount := FFileBuffer.Count; if bufcount > 0 then begin i := 0; while (i < bufcount - 1 )do begin Section := Trim(FFileBuffer[i]); if Section <> '' then if IsSection(Section) then begin Delete(Section, 1, 1); Delete(Section, Length(Section), 1); Strings.Add(Section); end; Inc(i); end; end; finally Strings.EndUpdate; end; end;
{ Ищет строку и удаляет ее (учитывая переносы) и возвращает индекс удаленной строки, если строка не найдена, возвращает -1. Поиск осуществляется по принципу маски, т.е. будет удалена первая найденная строка, начанающаяся с Target } function TInfFile.RemoveStr(const Section: string; Target: string): integer;
function CompareStrMask(const Str: string; const Mask: string): boolean; begin Result := false; if Length(Str) < Length(Mask) then Exit; if Copy(UpperCase(Trim(Str)), 1, Length(Mask)) = UpperCase(Mask) then Result := true; end;
var i, j: integer; InSection: boolean; bufcount: integer; FirstDelIndex, removecount: integer; // для перенесенных строк Current: string; tmp: string; begin Result := -1; // если строка не будет найдена bufcount := FFileBuffer.Count; if bufcount > 0 then begin if FFileBuffer.Find(Target, i) then // строка полностью совпадает begin FFileBuffer.Delete(i); Result := i; Exit; end; i := GetSectionIndex(Section); // поиск по маске if (i <> -1) then begin Inc(i); InSection := true; while (i < BufCount) do begin Current := ParseComments(FFileBuffer[i]); if (Current = '') then Inc(i) else begin if IsSection(Current) then if Current <> '['+Section+']' then InSection := false else InSection := true; if not InSection then // мы находмися в заданной секции? Inc(i) else if Current[Length(Current)] = '\' then // строка перенесена begin FirstDelIndex := i; tmp := ''; removecount := 1; while (Current[Length(Current)] = '\') do begin Delete(Current, Length(Current), 1); tmp := tmp + Current; Inc(i); Current := ParseComments(Trim(FFileBuffer[i])); if Current = '' then while ((Current = '') and (i < BufCount)) do begin Inc(i); Inc(removecount); Current := ParseComments(Trim(FFileBuffer[i])); end; Inc(removecount); end; tmp := tmp + Current; if CompareStrMask(tmp, Target) then begin for j := 1 to removecount do FFileBuffer.Delete(FirstDelIndex); Result := FirstDelIndex; // индекс удаленной строки Exit; // выходим после удаления end; Inc(i); end else if CompareStrMask(Current, Target) then begin FFileBuffer.Delete(i); Result := i; // индекс удаленной строки Exit; // выходим после удаления end else // if CompareStrMask(Current, Target) Inc(i); end; // if (Current = '') end; end;// if (i <> -1) // заданная секция отсутствует end; end;
{ Фукция читает все строки из определенной секции и записывает в буфер Strings также производит форматирование строк с переносами }
procedure TInfFile.ReadStrings(const Section: string; Strings: TStrings); var i: integer; InSection: boolean; BufCount: integer; FirstIndex: integer; Current, tmp: string; begin Strings.BeginUpdate; try Strings.Clear; BufCount := FFileBuffer.Count; if BufCount > 0 then begin FirstIndex := GetSectionIndex(Section); if FirstIndex <> -1 then begin i := FirstIndex+1; InSection := true; while (i < BufCount) do begin Current := ParseComments(Trim(FFileBuffer[i])); if (Current <> '') then begin if IsSection(Current) then begin if Current = '['+Section+']' then InSection := true else InSection := false; Inc(i); end else // if IsSection(Current) if InSection then begin if Current[Length(Current)] = '\' then begin tmp := ''; while (Current[Length(Current)] = '\') do begin Delete(Current, Length(Current), 1); tmp := tmp + Current; Inc(i); Current := ParseComments(Trim(FFileBuffer[i])); if Current = '' then while ((Current = '') and (i < BufCount)) do begin Inc(i); Current := ParseComments(Trim(FFileBuffer[i])); end; end; tmp := tmp + Current; Strings.Add(tmp); Inc(i); end else // if Current[Length(Current)] = '\' begin Strings.Add(Current); Inc(i); end; end else // if InSection Inc(i); end else // if (Current <> '') Inc(i); end; // while (i < BufCount) end; // if FirstIndex <> -1 end; // if BufCount > 0 finally Strings.EndUpdate; end; end;
{ функция записывает строку в заданную секцию } procedure TInfFile.WriteString(const Section: string; const Ext: string); var i: integer; begin i := GetSectionIndex(Section); if (i <> -1) then begin Inc(i); FFileBuffer.Insert(i, Ext); end else begin // если секции не существует, добавляем новую FFileBuffer.Add(''); FFileBuffer.Add('['+Section+']'); FFileBuffer.Add(Ext); end; end;
{ Функция записывает строки в заданную секцию } procedure TInfFile.WriteStrings(const Section: string; Strings: TStrings); var i, j, count: integer; begin i := GetSectionIndex(Section); if (i <> -1) then begin Inc(i); count := Strings.Count; if count > 0 then for j := 0 to count-1 do FFileBuffer.Insert(i, Strings[j]); end else begin FFileBuffer.Add(''); FFileBuffer.Add('['+Section+']'); FFileBuffer.AddStrings(Strings); end; end;
{ Функция заменяет строку в файле (учитывая переносы) } procedure TInfFile.ReplaceString(const Section: string; const Old: string; const New: string); var i: integer; index: integer; begin i := GetSectionIndex(Section); if (i <> -1) then begin index := RemoveStr(Section, Old); if (index <> -1) then FFileBuffer.Insert(index, New) else WriteString(Section, New); end else begin FFileBuffer.Add(''); FFileBuffer.Add('['+Section+']'); FFileBuffer.Add(New); end; end;
{ Удаление строки по маске (см. ф-ию RemoveStr) } procedure TInfFile.DeleteString(const Section: string; Target: string); begin RemoveStr(Section, Target); end;
{ Функция заменяет строку в файле на последовательность строк (удаляет строку Old и добавляет новые строки в начало секции) (удобно для записи многострочных (с переносами) параметров) } procedure TInfFile.ReplaceStrings(const Section: string; const Old: string; const New: TStrings); var i, j: integer; index: integer; begin i := GetSectionIndex(Section); if (i <> -1) then begin RemoveStr(Section, Old); WriteStrings(Section, New); end else begin FFileBuffer.Add(''); FFileBuffer.Add('['+Section+']'); FFileBuffer.AddStrings(New); end; end;
end.
|
Для описания ф-ий см. комментарии...
Надеюсь, что кому-нить может и пригодится 
ЗЫ Если вы нашли ошибку/баг в моем модуле, пожалуйста, сообщайте... |