问题
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 useFIni.ReadSectionValues
instead ofReadSection
. - Ran the modified application, and clicked on the
C Section
item to see how it displayed, and then the newrichardmarx
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