Ini Files - Read Multi Lines?

Deadly 提交于 2019-12-18 03:39:25

问题


I am aware that Ini Files are meant for single lines of information, needless to say I am trying to read/write multi lines to and from the Ini - without much success (I always seem to do things the hard way!)

Lets say my Ini File when saved, looks like this:

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

Suppose the Ini File is built dynamically (ie, the richardmarx and the Filenames are not know, but unique - they could literally be anything).

How would I be able to read the Ini File?

In this example then, how could I put richardmarx into a TEdit, and the Filenames associated with richardmarx section into a memo?

Many thanks in advance.


回答1:


Do not store multi-line strings into an INI file to begin with. Escape the line breaks, like @RobertFrank suggested. I would not use an asterik for that, though, as that is a valid text character. I would use something like this instead:

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

You can then read the string and replace the %n sequences with the value of the sLineBreak global variable. If you needed to store an actual % character, escape it as %%, eg:

[sales] 
value=Sale! 50%% off%nat Macy's



回答2:


You're not using a valid .ini format, so it's not going to be easy. It's much easier if you use a properly formed .ini file.

A valid ini file is of the format

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

Here's a sample of reading multiple lines from an ini file. While it uses a TListBox instead of a TEdit, it should be enough to get you started.

The code below will work with an improperly formatted file as well, but you'll probably have to change the code in the ListBox1Click event to use ReadSectionValues instead and do some manual parsing for each item before displaying them; in that case, create another TStringList in the event handler and pass it instead of Memo1.Lines.

With a properly formatted ini file, you can use TIniFile.ReadSection or TMemIniFile.ReadSections to load all of the sections into a TStrings descendant, and then use ReadSection(SectionName) to get each section's values.

Here's an example - save this ini file somewhere (I've used 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

Here's a sample of the form's code:

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.

Clicking on each section name in the ListBox displays the items that are in that section, as seen below:

EDIT: OK. I got curious to see how it would work with the ini file content you posted in your question.

So I made the following change:

  • Copied and pasted your sample ini content verbatim as a new section to the end of the Sample.ini I created above.

Ran the code, and clicked the new richardmarx item. Here's what I got:

Obviously, that wouldn't work. So I made the following additional changes:

  • Changed the ListBox1Click event to use FIni.ReadSectionValues instead of ReadSection.
  • Ran the modified application, and clicked on the C Section item to see how it displayed, and then the new richardmarx item to see how it displayed. The results are as follows:




回答3:


Pre-process the .ini file! Change all line-breaks between ] and [ to some character that will never appear in a filename (like asterisk). Then use TInifile to access the file you just preprocessed, changing the asterisks back to line breaks after you retrieve the strings. (Use StringReplace)

It's a little more complicated than that if you have more than one identifier in a section. In that case, you could use the equals sign as a flag that the preceding line break should not be removed. Maybe you read the file from the end towards the beginning.

You could even create a descendant of TIniFile that did the astrerisk-to-linebreak change for you.

No, this is certainly not an elegant solution. But, sometimes brute force like this works if you're stuck! The other solutions here are probably better, but thought I'd share this anyway in case it gives you a direction to think about heading...




回答4:


Using any TStrings descendent object you can just use the CommaText property to read and write all the lines as a single string.

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

and

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

CommaText is smart enough to handle lines containing commas by automatically embracing them in quotes.




回答5:


this code show you how to write and read multi lines with INI file

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.



回答6:


I wanted something similar, to include the body of an automated email in the ini file.
But I also wanted blank lines in the body. Here is part of the ini file

[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.

Here is what I ended up doing

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] := '';



回答7:


@Ken White

FIni.ReadSectionValues get full string after same key, like "key=value".

In .ini like

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

and after

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

Memo1.Lines will be {'Item1=Item B1', 'Item4=Item B4'}


Unfortunately, we can't read values directly, without any key. But in fact, it's not too difficult:

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 would be {'Item B1', 'Item B4'}

Upper method will read all values in section, without keys. How to read not-keyed values - I don't know (if at all it possible in .ini). As a solution for huge non-format .ini, just numerate all strings - read file like text file, find needed section and add same count index like a key, schematically(!):

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

If need mixed format, will require more complicated parser.

P.S. Sorry for my english, I'm not strong in it.




回答8:


You can use ReadSections method to get all the section names in ini file.

And perhaps you can use TMemIniFile's ReadSectionValues method to read "multiline values" (ie to read whole section into stringlist). If that doesn't work then perhaps you could use GetStrings to get the content of the ini file and parse it "semy manually" - IIRC it returns you a stringlist with section names where each item's object holds another stringlist with section data.



来源:https://stackoverflow.com/questions/8088108/ini-files-read-multi-lines

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!