Ini Files - читать несколько строк? - PullRequest
7 голосов
/ 11 ноября 2011

Мне известно, что файлы Ini предназначены для отдельных строк информации, разумеется, я пытаюсь читать / писать несколько строк в и из Ini - без особого успеха (мне всегда кажется, что я делаю трудные вещи!)

Допустим, мой файл Ini при сохранении выглядит следующим образом:

[richardmarx] 
Filenames=hazard
children of the night
right here waiting

Предположим, файл Ini создается динамически (т. Е. Richardmarx и имена файлов не известны, но уникальны - ониможет буквально быть чем угодно).

Как я смогу прочитать файл Ini?

В этом примере, как я могу поместить richardmarx в TEdit и имена файлов, связанные с разделом richardmarxв записку?

Большое спасибо заранее.

Ответы [ 8 ]

14 голосов
/ 11 ноября 2011

Не храните многострочные строки в INI-файле для начала.Избегайте разрывов строк, как предложено @RobertFrank.Я бы не использовал звездочку для этого, так как это допустимый текстовый символ.Вместо этого я бы использовал что-то вроде этого:

[richardmarx] 
Filenames=hazard%nchildren of the night%nright here waiting

Затем вы можете прочитать строку и заменить последовательности %n значением глобальной переменной sLineBreak.Если вам нужно было сохранить действительный символ %, экранируйте его как %%, например:

[sales] 
value=Sale! 50%% off%nat Macy's
6 голосов
/ 11 ноября 2011

Вы не используете правильный формат .ini, так что это будет непросто. Гораздо проще, если вы используете правильно сформированный файл .ini.

Допустимый ini файл имеет формат

[section]
akey=value
bkey=value
ckey=value

Вот пример чтения нескольких строк из INI-файла. Хотя он использует TListBox вместо TEdit, этого должно быть достаточно для начала работы.

Приведенный ниже код также будет работать с неправильно отформатированным файлом, но вам, вероятно, придется изменить код в событии ListBox1Click, чтобы использовать вместо него ReadSectionValues, и выполнить некоторый ручной анализ для каждого элемента перед его отображением; в этом случае создайте еще один TStringList в обработчике событий и передайте его вместо Memo1.Lines.

В правильно отформатированном файле ini вы можете использовать TIniFile.ReadSection или TMemIniFile.ReadSections, чтобы загрузить все разделы в потомок TStrings, а затем использовать ReadSection(SectionName), чтобы получить значения каждого раздела.

Вот пример - сохраните этот файл ini где-нибудь (я использовал d:\temp\sample.ini:

[A Section]
Item1=Item A1
Item2=Item A2
Item3=Item A3
Item4=Item A4

[B Section]
Item1=Item B1
Item2=Item B2
Item3=Item B3
Item4=Item B4

[C Section]
Item1=Item C1
Item2=Item C2
Item3=Item C3
Item4=Item C4

Вот пример кода формы:

unit Unit2;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, IniFiles;

type
  TForm2 = class(TForm)
    ListBox1: TListBox;
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure ListBox1Click(Sender: TObject);
  private
    { Private declarations }
    FIni: TMemIniFile;
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.dfm}

const
  IniName = 'd:\Temp\Sample.ini';


procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FIni.Free;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  FIni := TMemIniFile.Create(IniName);
  Memo1.Lines.Clear;
  FIni.ReadSections(ListBox1.Items);
end;

procedure TForm2.ListBox1Click(Sender: TObject);
var
  Section: string;
begin
  if ListBox1.ItemIndex > -1 then
  begin
    Section := ListBox1.Items[ListBox1.ItemIndex];
    FIni.ReadSection(Section, Memo1.Lines);
  end;
end;

end.

При нажатии на название каждого раздела в ListBox отображаются элементы, которые находятся в этом разделе, как показано ниже:

Application Screen Capture

РЕДАКТИРОВАТЬ: ОК. Мне стало любопытно посмотреть, как это будет работать с содержимым ini-файлов, которое вы разместили в своем вопросе.

Итак, я сделал следующее изменение:

  • Копирование и вставка вашего образца ini content дословно как нового раздела до конца Sample.ini, который я создал выше.

Запустил код и щелкнул новый элемент richardmarx. Вот что я получил:

Sample image 2

Очевидно, это не сработает. Поэтому я внес следующие дополнительные изменения:

  • Изменено событие ListBox1Click для использования FIni.ReadSectionValues вместо ReadSection.
  • Запустил измененное приложение и щелкнул элемент C Section, чтобы увидеть, как оно отображается, а затем новый элемент richardmarx, чтобы увидеть, как оно отображается. Результаты следующие:

Image with C Section selectedImage with richardmarx selected

4 голосов
/ 11 ноября 2011

Предварительная обработка файла .ini! Измените все разрывы строк между] и [на некоторые символы, которые никогда не появятся в имени файла (например, звездочка). Затем используйте TInifile для доступа к файлу, который вы только что обработали, изменив звездочки на разрывы строк после извлечения строк. (Используйте StringReplace )

Это немного сложнее, чем если у вас есть более одного идентификатора в разделе. В этом случае вы можете использовать знак равенства в качестве флага, что предыдущий разрыв строки не должен быть удален. Может быть, вы читаете файл с конца в начало.

Вы могли бы даже создать потомка TIniFile, который бы изменил для вас переход на новую линию.

Нет, это, конечно, не элегантное решение. Но иногда грубая сила, как это работает, если вы застряли! Другие решения здесь, вероятно, лучше, но я подумаю, что я все равно поделюсь этим, если это даст вам направление к заголовку ...

2 голосов
/ 13 июля 2013

этот код показывает, как писать и читать многострочный файл INI

procedure TForm1.SaveButtonClick(Sender: TObject);
begin
  IniFile.WriteString('Name', 'FirtName', FirstName.Text);
  IniFile.WriteString('Name', 'LastName', LastName.Text);
  IniFile.WriteInteger('Alpha Blend', 'Alpha Blend Value', Form1.AlphaBlendValue);
//Here start save Memo Lines
  LinesCount := AboutMemo.Lines.Count;
  IniFile.WriteInteger('About', 'Lines Count', LinesCount);
  for I := 0 to LinesCount-1 do
  IniFile.WriteString('About', 'About'+IntToStr(I), AboutMemo.Lines[i]);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin;
  GetCurrentDir;
  IniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  FirstName.Text := IniFile.ReadString('Name', 'FirstName', '');
  LastName.Text := IniFile.ReadString('Name', 'LastName', '');
  Form1.AlphaBlendValue := IniFile.ReadInteger('Alpha Blend', 'Alpha Blend Value', 255);
//Here Start Read Memo Lins From INI File
  LinesCount := IniFile.ReadInteger('About', 'Lines Count', 0);
  for I := 0 to LinesCount-1 do
  AboutMemo.Lines.Insert(I, IniFile.ReadString('About', 'About'+IntToStr(I), ''));
end;
end.
2 голосов
/ 11 ноября 2011

Используя любой TStrings дочерний объект, вы можете просто использовать свойство CommaText для чтения и записи всех строк в виде одной строки.

MyStrings.CommaText := IniFile.ReadString('Section', 'Ident');

и

IniFile.WriteString('Section', 'Ident', MyStrings.CommaText);

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

1 голос
/ 30 декабря 2011

@ Кен Уайт

FIni.ReadSectionValues ​​ получить полную строку после того же ключа, например, "ключ = значение".

В .ini как

[B Section]
Item1=Item B1
Item B2
Item B3
Item4=Item B4

и после

FIni.ReadSectionValues('B Section',Memo1.Lines);

Memo1.Lines будет {'Item1 = Item B1', 'Item4 = Item B4'}


К сожалению, мы не можем читать значения напрямую, без ключа. Но на самом деле это не так уж сложно:

procedure TForm2.FormCreate(Sender: TObject);
var i:longint;
strtemp: TStringList;
begin
  FIni := TMemIniFile.Create(IniName);
  Memo1.Lines.Clear;
  Fini.ReadSection('B Section',strtemp);
for i:=0 to strtemp.Count-1 do
  Memo1.Items.Add(Fini.ReadString('B Section',strtemp[i],''));
end;

Memo1.Lines будет {'Item B1', 'Item B4'}

Верхний метод будет читать все значения в разделе без ключей. Как читать неключевые значения - я не знаю (если это вообще возможно в .ini ). В качестве решения для огромного неформатного .ini , просто пронумеруйте все строки - прочитайте файл как текстовый файл, найдите нужный раздел и добавьте тот же индекс числа как ключ, схематически (!):

FileString[i]:=inttostr(i)+'='+FileString[i];

Если нужен смешанный формат, потребуется более сложный парсер.

P.S. Извините за мой английский, я не силен в этом.

1 голос
/ 12 ноября 2011

Я хотел что-то подобное, чтобы включить тело автоматизированной электронной почты в INI-файл.
Но я также хотел пустые строки в теле.Вот часть INI-файла

[EmailBody]
This is the weekly Survey Status notification email.
.
It lists Survey Reports that may need some attention.
.
The purpose of this information is to improve conduct-of-operations in our tracking, 
recordkeeping, and maintenance of Survey Reports.  This report will also enable 
us to identify any changes made to electronic Surveys following the normal QA review.
.
Please review your information weekly and determine if you need to take corrective action.

Вот что я в итоге сделал

var
  Config: TMemIniFile;
  sl: TStringList;
...
  sl := TStringList.Create;
  Config.ReadSectionValues('EmailBody', sl);
  for i := 0 to sl.Count - 1 do
    if sl[i] = '.' then
      sl[i] := '';
0 голосов
/ 11 ноября 2011

Вы можете использовать ReadSections метод, чтобы получить все имена разделов в INI-файле.

И, возможно, вы можете использовать TMemIniFile ReadSectionValues ​​ методчитать "многострочные значения" (т.е. читать весь раздел в список строк).Если это не сработает, возможно, вы могли бы использовать GetStrings , чтобы получить содержимое ini-файла и проанализировать его «вручную в semy» - IIRC возвращает вам список строк с именами разделов, где объект каждого элемента содержит другой список строкс данными раздела.

...