StringBuilder.Split in Delphi? - delphi

Anyone know of a good Split procedure that uses StringBuilder in Delphi?

You might be better off using TStringlist.DelimitedText (or any other non-abstract TStrings sub-class). It's more of the traditional Delphi way of achieving what string.Split does in .Net (assuming I remember correctly).
e.g. To split on a pipe | character
var
SL : TStrings;
i : integer;
begin
SL := TStringList.Create;
try
SL.Delimiter := '|';
SL.StrictDelimiter := True;
SL.DelimitedText := S;
for i := SL.Count - 1 do
begin
// do whatever with sl[i];
end;
finally
SL.Free;
end;
end;
You may need to handle the QuoteChar property as well

You can also Look at my answer to this question for a general purpose utility functions GetStringPart and NumStringParts that allow you to perform split type operations.

Related

how to speed up accessing the Fields collection in Word by using Delphi

I need change fields's content,but this way is take too much time,how can i do? Anyone can help me
procedure TForm1.Button1Click(Sender: TObject);
var
WordApp, WordDocument, Field: OleVariant;
I: Integer;
begin
WordApp := CreateOleObject('Word.Application');
try
WordDocument := WordApp.Documents.Open('C:\MyDoc.doc');
if WordDocument.Fields.Count >= 1 then
for I := 1 to WordDocument.Fields.Count do
begin
**Field := WordDocument.Fields.Item(I); // file size:20M,Fields.Count:30, This step takes 3 seconds**
end;
finally
WordApp.Quit;
end;
end;
Please use early binding as shown here which one is better in terms of performance, the early binding or late binding in Delphi COM objects
The performance increase is significant.

Copy a file to clipboard in Delphi

I am trying to copy a file to the clipboard. All examples in Internet are the same. I am using one from, http://embarcadero.newsgroups.archived.at/public.delphi.nativeapi/200909/0909212186.html but it does not work.
I use Rad Studio XE and I pass the complete path. In mode debug, I get some warnings like:
Debug Output:
Invalid address specified to RtlSizeHeap( 006E0000, 007196D8 )
Invalid address specified to RtlSizeHeap( 006E0000, 007196D8 )
I am not sure is my environment is related: Windows 8.1 64 bits, Rad Studio XE.
When I try to paste the clipboard, nothing happens. Also, seeing the clipboard with a monitor tool, this tool shows me error.
The code is:
procedure TfrmDoc2.CopyFilesToClipboard(FileList: string);
var
DropFiles: PDropFiles;
hGlobal: THandle;
iLen: Integer;
begin
iLen := Length(FileList) + 2;
FileList := FileList + #0#0;
hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
SizeOf(TDropFiles) + iLen);
if (hGlobal = 0) then raise Exception.Create('Could not allocate memory.');
begin
DropFiles := GlobalLock(hGlobal);
DropFiles^.pFiles := SizeOf(TDropFiles);
Move(FileList[1], (PChar(DropFiles) + SizeOf(TDropFiles))^, iLen);
GlobalUnlock(hGlobal);
Clipboard.SetAsHandle(CF_HDROP, hGlobal);
end;
end;
UPDATE:
I am sorry, I feel stupid. I used the code that did not work, the original question that somebody asked, in my project, while I used the Remy's code, the correct solution, here in Stackoverflow. I thought that I used the Remy's code in my project. So, now, using the Remy's code, everything works great. Sorry for the mistake.
The forum post you link to contains the code in your question and asks why it doesn't work. Not surprisingly the code doesn't work for you any more than it did for the asker.
The answer that Remy gives is that there is a mismatch between ANSI and Unicode. The code is for ANSI but the compiler is Unicode.
So click on Remy's reply and do what it says: http://embarcadero.newsgroups.archived.at/public.delphi.nativeapi/200909/0909212187.html
Essentially you need to adapt the code to account for characters being 2 bytes wide in Unicode Delphi, but I see no real purpose repeating Remy's code here.
However, I'd say that you can do better than this code. The problem with this code is that it mixes every aspect all into one big function that does it all. What's more, the function is a method of a form in your GUI which is really the wrong place for it. There are aspects of the code that you might be able to re-use, but not factored like that.
I'd start with a function that puts an known block of memory into the clipboard.
procedure ClipboardError;
begin
raise Exception.Create('Could not complete clipboard operation.');
// substitute something more specific that Exception in your code
end;
procedure CheckClipboardHandle(Handle: HGLOBAL);
begin
if Handle=0 then begin
ClipboardError;
end;
end;
procedure CheckClipboardPtr(Ptr: Pointer);
begin
if not Assigned(Ptr) then begin
ClipboardError;
end;
end;
procedure PutInClipboard(ClipboardFormat: UINT; Buffer: Pointer; Count: Integer);
var
Handle: HGLOBAL;
Ptr: Pointer;
begin
Clipboard.Open;
Try
Handle := GlobalAlloc(GMEM_MOVEABLE, Count);
Try
CheckClipboardHandle(Handle);
Ptr := GlobalLock(Handle);
CheckClipboardPtr(Ptr);
Move(Buffer^, Ptr^, Count);
GlobalUnlock(Handle);
Clipboard.SetAsHandle(ClipboardFormat, Handle);
Except
GlobalFree(Handle);
raise;
End;
Finally
Clipboard.Close;
End;
end;
We're also going to need to be able to make double-null terminated lists of strings. Like this:
function DoubleNullTerminatedString(const Values: array of string): string;
var
Value: string;
begin
Result := '';
for Value in Values do
Result := Result + Value + #0;
Result := Result + #0;
end;
Perhaps you might add an overload that accepted a TStrings instance.
Now that we have all this we can concentrate on making the structure needed for the CF_HDROP format.
procedure CopyFileNamesToClipboard(const FileNames: array of string);
var
Size: Integer;
FileList: string;
DropFiles: PDropFiles;
begin
FileList := DoubleNullTerminatedString(FileNames);
Size := SizeOf(TDropFiles) + ByteLength(FileList);
DropFiles := AllocMem(Size);
try
DropFiles.pFiles := SizeOf(TDropFiles);
DropFiles.fWide := True;
Move(Pointer(FileList)^, (PByte(DropFiles) + SizeOf(TDropFiles))^,
ByteLength(FileList));
PutInClipboard(CF_HDROP, DropFiles, Size);
finally
FreeMem(DropFiles);
end;
end;
Since you use Delphi XE, strings are Unicode, but you are not taking the size of character into count when you allocate and move memory.
Change the line allocating memory to
hGlobal := GlobalAlloc(GMEM_SHARE or GMEM_MOVEABLE or GMEM_ZEROINIT,
SizeOf(TDropFiles) + iLen * SizeOf(Char));
and the line copying memory, to
Move(FileList[1], (PByte(DropFiles) + SizeOf(TDropFiles))^, iLen * SizeOf(Char));
Note the inclusion of *SizeOf(Char) in both lines and change of PChar to PByte on second line.
Then, also set the fWide member of DropFiles to True
DropFiles^.fWide := True;
All of these changes are already in the code from Remy, referred to by David.

How to align paragraph using Delphi MSWord automation?

I want to align a text to center and I have no idea on how to achieve it.
Here is my code:
try
MsWord := GetActiveOleObject('Word.Application');
except
try
MsWord := CreateOleObject('Word.Application');
MsWord.Visible := True;
except
Exception.Create('Error');
end;
end;
MSWord.Documents.Add;
MSWord.Selection.Font.Size := 22;
MSWord.Selection.Font.Bold := true;
MSWord.Selection.TypeText(#13#10);
MSWord.Selection.TypeText('I want this to be center-aligned');
...
MSWord.ActiveDocument.SaveAs('C:\doc2.doc');
Please help.
Thanks
This works for me:
procedure TForm1.Button1Click(Sender: TObject);
var
MSWord : OleVariant;
begin
try
MsWord := GetActiveOleObject('Word.Application');
except
try
MsWord := CreateOleObject('Word.Application');
MsWord.Visible := True;
except
Exception.Create('Error');
end;
end;
MSWord.Documents.Add;
MSWord.Selection.Font.Size := 22;
MSWord.Selection.Font.Bold := true;
MSWord.Selection.TypeText(#13#10);
MSWord.Selection.TypeText('I want this to be center-aligned');
MSWord.Selection.ParagraphFormat.Alignment := wdAlignParagraphCenter;
MSWord.ActiveDocument.SaveAs('C:\doc2.doc');
end;
Btw, the way to find the answer yourself is to go into Word, start recording a macro, perform the action, stop recording then edit the macro to see what code Word generates. Converting that to Delphi is usually fairly trivial if you're using late binding (accessing Word from Delphi via an OleVariant) but can be a bit long winded if you're using early binding, because early binding requires all parameters to be specified, whereas late binding lets you leave most of them out.

Reading value without using sections

How do I read value from INI file without using sections?
So instead of normal file:
[section]
name=value
it would result in this:
name=value
I wouldn't call it an INI file, then. Anyhow, for this the TStringList class fits perfectly.
Consider the file animals.txt:
dog=Sally
rat=Fiona
cat=Linus
And consider this code:
procedure TForm1.Button1Click(Sender: TObject);
begin
with TStringList.Create do
try
LoadFromFile('C:\Users\Andreas Rejbrand\Desktop\animals.txt');
ShowMessage(Values['dog']);
finally
Free;
end;
end;
There's a nice tutorial over here. For example, if iniFile is an instance of TIniFile, you can call the iniFile.ReadString method with an empty section specifier.
This is a late answer but here is some code I wrote for my project:
function GetPropertyValue(aFile, Key: string): string;
var
properties: TStringList;
begin
properties := TStringList.Create;
try
properties.LoadFromFile(aFile);
Result := properties.Values[key];
finally
properties.free;
end;
end;
procedure SetPropertyValue(aFile, Key, Value: string);
var
I: Integer;
properties: TStringList;
found: Boolean;
begin
found := False;
properties := TStringList.Create;
try
properties.LoadFromFile(aFile);
for I := 0 to properties.Count -1 do
begin
if properties.Names[I] = Key then
begin
properties[I] := Key + '=' + Value;
found := True;
Break
end;
end;
if not found then
begin
properties.Add(Key + '=' + Value);
end;
finally
properties.SaveToFile(aFile);
properties.free;
end;
end;
I think the question really needs more information. Often people will ask questions relating to what they think they need to do instead of asking questions related to what they are actually trying to accomplish.
Why do you need to do this instead of using the normal methods of reading the ini entries?
If these are existing ini files, then you should use the Tinifile.ReadSections to read the section names into a stringlist and then iterate through that list using Tinifile.ReadSectionValues to read all the section name/values pairs.
Are you reading existing INI files, or reading and writing your own files?
If these are your own files, then Andreas has a good answer above.

Dump content of TCustomActionList descendant into human-readable form

I have foreign object instance and i want to examine properties of the component "manually".
I know about component streaming and how to convert output to text format, but problem is - TCustomActionList (as i figured out my instance is TActionManager) wont stream its actions. Is there any trick to have it to do so?
UPD: Now doing that by creating TDataModule (as container/owner), iterating thru Actions and creating TAction instances and doing Assign. Solution still suffers from not caring about TAction descendants tho.
Simple example that doesn't do anything tricky.
function TMyThing.SaveComponent(a:TComponent):String;
var
Stream2: TStringStream;
Stream1: TMemoryStream;
begin
Stream1 := TMemoryStream.Create;
Stream2 := TStringStream.Create;
try
Stream1.WriteComponent(a);
Stream1.position := 0;
ObjectBinaryToText(Stream1, Stream2);
result := Stream2.DataString;
finally
Stream1.Free;
Stream2.Free;
end;
end;
procedure TMyThing.Dump;
var
n:Integer;
a:TContainedAction;
s:String;
begin
for n := 0 to ActionManager1.ActionCount-1 do begin
a := ActionManager1.Actions[n];
s := '----- '+a.Category+' '+a.Name+' '+a.ClassName+':';
// crude manual way
if a is TAction then begin
s := s+ ' '+TAction(a).Caption;
s := s+ ' '+TAction(a).Hint;
end;
// RTTI stream in DFM text format
s := s+ SaveComponent(a);
Memo1.Lines.Add(s);
end;
end;

Resources