Pulling Text From a Memo Box Line by Line - database

I need to go through a ton of data that is stored in a paradox table within a Memo field. I need to process this data line by line and process each line.
How can I tell Delphi to fetch each line in the memo field one by one?
Could I use #13#10 as a delimiter?

Assuming that what is in the memo field uses #13#10 as the line separator then I would use a TStringList, and the very useful Text property to split the memo field text into separate lines:
var
StringList: TStringList;
Line: string;
.....
StringList.Text := MemoFieldText;
for Line in StringList do
Process(Line);
Even if your memo field uses Unix linefeeds then this code will interpret the memo field correctly.

It depends on how the field is actually declared in Paradox. If it's a TMemoField, it's pretty easy:
var
SL: TStringList;
Line: string;
begin
SL := TStringList.Create;
try
SL.Text := YourMemoField.GetAsString;
for Line in SL do
// Process each line of text using `Line`
finally
SL.Free;
end;
end;
If it's a TBlobField, it's a little more complicated. You need to read the memo field using a TBlobStream, and load the content of that stream into a TStringList:
// For Delphi versions that support it:
procedure LoadBlobToStringList(const DS: TDataSet; const FieldName: string;
const SL: TStringList);
var
Stream: TStream;
begin
Assert(Assigned(SL), 'Create the stringlist for LoadBlobToStringList!');
SL.Clear;
Stream := DS.CreateBlobStream(DS.FieldByName(FieldName), bmRead);
try
SL.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
// For older Delphi versions that do not have TDataSet.CreateBlobStream
procedure LoadBlobToStringList(const DS: TDataSet; const TheField: TField;
const SL: TStringList);
var
BlobStr: TBlobStream;
begin
Assert(Assigned(SL), 'Create the stringlist for LoadBlobToStringList!');
SL.Clear;
BlobStr := TBlobStream.Create(DS.FieldByName(TheField), bmRead);
try
SL.LoadFromStream(BlobStr);
finally
BlobStr.Free;
end;
end;
// Use it
var
SL: TStringList;
Line: string;
begin
SL := TStringList.Create;
LoadBlobToStringList(YourTable, YourMemoFieldName, SL);
for Line in SL do
// Process each Line, which will be the individual line in the blob field
// Alternatively, for earlier Delphi versions that don't support for..in
// declare an integer variable `i`
for i := 0 to SL.Count - 1 do
begin
Line := SL[i];
// process line of text using Line
end;
end;

Related

Drag & Drop Component Suite: Drop files and if path isn't exists get data from file

I use the Drag and Drop Component Suite for Delphi.
I try to create a drag & drop area which accepts files (ie, from Windows Explorer) and data (ie, from Outlook attachments). So, I use the demo (CombatTargetDemo) to learn how it works, and after this I create a wrapper class which creates a TDropComboTarget object:
constructor TDragAndDrop.Create( vpntOwner: TWinControl);
begin
fpntDragAndDrop := TDropComboTarget.Create(vpntOwner);
fpntDragAndDrop.Name := 'DropComboTarget_'+vpntOwner.Name;
fpntDragAndDrop.DragTypes := [dtCopy, dtLink];
fpntDragAndDrop.OnDrop := DropFiles;
fpntDragAndDrop.Target := vpntOwner;
fpntDragAndDrop.Formats := [mfFile, mfData];
end;
procedure TDragAndDrop.DropFiles(Sender: TObject; ShiftState: TShiftState; Point: TPoint; var Effect: Integer);
var
intCnt: Integer;
pntStream: TStream;
strFileName: String;
strDragAndDropFile: String;
begin
try
fstlDroppedFilePaths.Clear;
fstlDroppedFilePaths.Assign(fpntDragAndDrop.Files);
for intCnt := 0 to fpntDragAndDrop.Data.Count-1 do begin
strFileName := fpntDragAndDrop.Data.Names[intCnt];
if (strFileName = '') then begin
strFileName := IntToStr(intCnt)+'_'+FormatDateTime('yyyymmddhhnnss', Now())+'.dat';
end;
strDragAndDropFile := GetDragAndDropSavePath+strFileName;
pntStream := TFileStream.Create(strDragAndDropFile, fmCreate);
try
pntStream.CopyFrom(fpntDragAndDrop.Data[intCnt], fpntDragAndDrop.Data[intCnt].Size);
finally
pntStream.Free;
end;
if FileExists(strDragAndDropFile, false) then begin
fstlDroppedFilePaths.Add(strDragAndDropFile);
end;
end;
except
end;
end;
First of all, the code works.
If I drop a Windows Explorer file on the area:
fpntDragAndDrop.Files.Count is 1 (contains the path+name from file)
fpntDragAndDrop.Data.Count is 1 (contains the file as a stream)
If I drop a file from Outlook on the area:
fpntDragAndDrop.Files.Count is 0 (contains nothing)
fpntDragAndDrop.Data.Count is 1 (contains the file as a stream)
Now my problem:
If I drop very large files from Windows Explorer, the component does the following:
Read the file header and add an item to fpntDragAndDrop.Files
Create a TMemoryStream and try to load the data from the file into the stream
Step 1 is perfect, but on step 2 I get an exception because of insufficient memory.
My solution:
I want that the component does Step 1. If Step 1 gives a result, then the component should skip Step 2. After this, the variables in the DropFiles procedure should have the following values:
If I drop a Windows Explorer file on the area:
fpntDragAndDrop.Files.Count is 1 (contaims the path+name from the file)
fpntDragAndDrop.Data.Count is 0 (No memory stream is loaded)
If I drop a file from Outlook on the area:
fpntDragAndDrop.Files.Count is 0 (comtains nothing)
fpntDragAndDrop.Data.Count is 1 (contains the file as a stream)
Does somebody have an idea? Or maybe the component has a setting for that?
I'm not overly familiar with this suite, but just browsing through its source, I think you can use the OnAcceptFormat event to reject formats you don't want on a per-drop basis.
So, even though you have enabled drops of mfData doesn't mean you have to actually accept a dropped stream (TDataStreamDataFormat) if a file path (TFileDataFormat or TFileMapDataFormat) is available. So, query the fpntDragAndDrop.DataObject to see what formats it actually holds, such as by passing it to the HasValidFormats() method of the various formats in the fpntDragAndDrop.DataFormats property.
For example:
fpntDragAndDrop.OnAcceptFormat := AcceptStreams;
...
procedure TDragAndDrop.AcceptStreams(Sender: TObject;
const DataFormat: TCustomDataFormat; var Accept: boolean);
var
Fmt: TCustomDataFormat;
i: Integer;
begin
if DataFormat is TDataStreamDataFormat then
begin
// FYI, TFileDataFormat should be in DataFormats[0],
// and TFileMapDataFormat should be in DataFormats[5],
// if you want to avoid this loop...
for i := 0 to fpntDragAndDrop.DataFormats.Count-1 do
begin
Fmt := fpntDragAndDrop.DataFormats[i];
if (Fmt <> DataFormat) and ((Fmt is TFileDataFormat) or (Fmt is TFileMapDataFormat)) then
begin
if Fmt.HasValidFormats(fpntDragAndDrop.DataObject) then
begin
Accept := False;
Exit;
end;
end;
end;
end;
Accept := True; // should already be True by default...
end;

Delphi TRichEdit to Array and local storage

I'm looking for informations on how to put TRichEdit into an Array and save it to Local file (ex: File.dat).
The goal is to store a number of text, with a description, and the 'name' of it.
I think I have to start with:
type
TMessage = record
Name : string;
Desc : string;
Text : TMemoryStream;
end;
var ARListMessages: array[1..50] of TMessage
And add data with something like:
richedit.Lines.SaveToStream( ARListMessages[i].Text );
How to create the Array, and make manipulations on it (Add, remove
...) with the 'name'?
How can I save it (Array), and load it easily from local storage ? (Ex:
File.dat)
I've found some informations here, without beeing able to make something working.
Thanks for your time.
[EDIT 18/09/2017]
I'm Still looking to find a solution, and try to find a way to save my informations to a local file.
My actual code to test is :
var
MessageArray : array of TMessage;
// // // //
SetLength(MessageArray, 1);
MessageArray[0].Name := 'Hey You';
MessageArray[0].Desc := 'Im here and will stay here, just in case';
MessageArray[0].Text := TMemoryStream.Create;
MessageArray[0].Text.Position := 0;
RichEdit1.plaintext := false;
RichEdit1.Lines.SaveToStream( MessageArray[0].Text );
So, looking to save MessageArray, but haven't find how to do that yet.
I've take a look on SuperObjet, but can't find how to deal with it.
Omxl was looking Great and easy, but free trial ... :(
I've been able to have an answer.
I share it here if someone need the solution.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, System.Generics.Collections, Classes, System.Zip;
type
TPersonne = class
strict private
FNom : string;
FPrenom: string;
public
property Nom: string read FNom write FNom;
property Prenom: string read FPrenom write FPrenom;
end;
TFamille = class(TDictionary<string, TPersonne>)
private
procedure LoadFromStream(stream: TStream);
procedure SaveToStream(stream: TStream);
public
procedure LoadFromFile(const AFileName: string);
procedure SaveToFile(const AFileName: string);
end;
procedure TFamille.SaveToStream(stream: TStream);
var
i: Integer;
Personne: TPersonne;
Pair: System.Generics.Collections.TPair<string, TPersonne>;
Writer: TWriter;
begin
Writer := TWriter.Create(stream, 4096);
try
Writer.WriteListBegin;
for Pair in Self do
begin
Writer.WriteString(Pair.Key);
Writer.WriteListBegin;
Personne := Pair.Value;
Writer.WriteString(Personne.Nom);
Writer.WriteString(Personne.Prenom);
Writer.WriteListEnd;
end;
Writer.WriteListEnd;
finally
Writer.Free;
end;
end;
procedure TFamille.LoadFromStream(stream: TStream);
var
Personne: TPersonne;
Reader: TReader;
sKey: string;
begin
Clear;
Reader := TReader.Create(stream, 1024);
try
Reader.ReadListBegin;
while not Reader.EndOfList do
begin
sKey := Reader.ReadString;
Personne := TPersonne.Create;
Reader.ReadListBegin;
while not Reader.EndOfList do
begin
Personne.Nom := Reader.ReadString;
Personne.Prenom := Reader.ReadString;
end;
Reader.ReadListEnd;
Add(sKey, Personne);
end;
Reader.ReadListEnd;
finally
Reader.Free;
end;
end;
procedure TFamille.LoadFromFile(const AFileName: string);
var
Stream: TStream;
begin
Stream := TFileStream.Create(AFileName, fmOpenRead);
try
LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
procedure TFamille.SaveToFile(const AFileName: string);
var
stream: TStream;
begin
stream := TFileStream.Create(AFileName, fmCreate);
try
SaveToStream(stream);
finally
stream.Free;
end;
end;
var
Famille: TFamille;
Personne: TPersonne;
begin
try
Famille := TFamille.Create;
try
Personne := TPersonne.Create;
Personne.Nom := 'Julie';
Personne.Prenom := 'Albertine';
Famille.Add('1', Personne);
Famille.SaveToFile('D:\famille.txt');
finally
FreeAndNil(Famille);
end;
Famille := TFamille.Create;
try
Famille.LoadFromFile('D:\famille.txt');
if Famille.Count > 0 then
Writeln(Famille['1'].Nom + ' ' + Famille['1'].Prenom);
finally
FreeAndNil(Famille);
end;
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Thanks all for your Help !
How to create the Array, and make manipulations on it (Add, remove
...) with the 'name'?
From your first code the array is already created. No further work is needed. And even if you use dynamic arrays you still don't have to do any thing just declaring it will suffice.
If you are asking "How to create the array ?" as in "How to create such a list, so I could add, remove with the 'name' field" then I would suggest TDictionary (instead of the city names use your field 'name' as a key) to do the manipulations and then when saving use this
How can I save it (Array), and load it easily from local storage ?
(Ex: File.dat)
You can't directly just like array.savetofile;.You need to use one of file handling methods like TFileStream to store the array.
Note: in your edit the memory stream will cause you only trouble because each time you create it you will need to free it after its use instead use these functions to extract formatted text and change the field Text : TMemoryStream; to Text : string;
RichTextToStr: Convert the rich format text to a string.
function RichTextToStr(RichEdit:TRichEdit):string;
var
SS : TStringStream;
begin
SS := TStringStream.Create('');
try
RichEdit.Lines.SaveToStream(SS);
Result := SS.DataString;
finally
SS.Free;
end;
end;
loadToRichedit: Load the formatted text to the RichEdit again.
procedure loadToRichedit(Const St:string;RichEdit:TRichEdit);
var
SS : TStringStream;
begin
SS := TStringStream.Create(St);
try
RichEdit.Lines.LoadFromStream(SS);
finally
SS.Free;
end;
end;

Add field to FDMemTable in Delphi

I am coding something with DBGrid and there is not enough data witch i cen get with FDQuery. I would like custom data beside "FDQuery" data. I found component that should be able to do this and it is called FDMemTable. I can get data from FDQuery to FDMemTable, but I cant add a new field where I can put different data. So my question is how to propper connect the data with FDQuery and add extra column in FDMemTable.
procedure TWorkflowDM.Temp;
var
Error: string;
Temp: string;
begin
try
FDQuery1.Open;
FDQuery1.FetchAll;
FDMemTable1.Data:= FDQuery1.Data;
FDMemTable1.FieldDefs.Add('Test', ftString, 20, False); <-ERROR (Error 'FDMemTable1: Field ''Test'' not found')
FDMemTable1.Open;
FDMemTable1.First;
while not FDMemTable1.Eof do
begin
Temp:= FDMemTable1.FieldByName('Test').AsString;
FDMemTable1.Next;
end;
except
on E: Exception do
Error:= E.Message;
end;
end;
We copy the field definitions from the source DataSet and append the additional fields. Then we call CreateDataset or optionally set Active to true. This creates all the necessary fields and opens the FDMemTable. Then we populate it by CopyDataset method. This code works:
procedure TWorkflowDM.Temp;
var
Error: string;
Temp: string;
begin
try
FDQuery1.Open;
// FDQuery1.FetchAll;
FDMemTable1.FieldDefs := FDQuery1.FieldDefs;
FDMemTable1.FieldDefs.Add('Test', ftString, 20{, False}); // default parameter
FDMemTable1.CreateDataSet;//or just Open that sets Active to true;
FDMemTable1.CopyDataSet(FDQuery1);
FDMemTable1.First;
while not FDMemTable1.Eof do
begin
Temp := FDMemTable1.FieldByName('Test').AsString;
FDMemTable1.Next;
end;
except
on E: Exception do
Error := E.Message;
end;
end;

TClientDataSet read Binary field to TStream

i have tried this every way that I possible can, but cannot seem to resolve this. I am working with DBExress in Delphi XE3 writing a REST DataSnap Server.
I have data stored in MSQL in a Binary(384) field and Binary is as far as I know that same as a BLOB/Image field as it is all Binary data.
When trying to stream this data to a TStream I receive an exception error and have tried the following
var
STemplate : TStream;
begin
......
Template := TBlobField.Create(cdsBSUserTemplates.FieldByName('bTemplate'));
TBlobField(cdsBSUserTemplates.FieldByName('bTemplate')).SaveToStream(STemplate); //exception
......
end;
and I have tried
var
STemplate : TStream;
begin
......
Template := TBlobField.Create(cdsBSUserTemplates.FieldByName('bTemplate'));
STemplate := cdsBSUserTemplates.CreateBlobStream(Template, bmRead); //exception
......
end;
I can return the value .AsString, but it is Bytes and then I need to try and fix what I have read from that field.
Any idea what else I can try?
You're working much too hard. :-)
You need to properly create the stream, and then just let the field write to it.
var
Output: TMemoryStream;
Fld: TBlobField;
begin
// Use of variable makes it more readable
Fld := cdsBSUserTemplates.FieldByName('bTemplate') as TBlobField;
Output := TMemoryStream.Create;
try
Fld.SaveToStream(Output);
Output.Position := 0;
// Do whatever with the output stream
finally
Output.Free;
end;
end;
After your comment that you might not be using a TBlobField (which would have been nice to know before I posted my answer), you can try this instead (untested, because I clearly don't have your data):
var
Output: TMemoryStream;
Fld: TField;
Bytes: TArray<Byte>;
begin
Fld := ADOQuery1.FieldByName('bTemplate');
Output := TMemoryStream.Create;
try
if Fld.IsBlob then
TBlobField(Fld).SaveToStream(Output)
else
begin
Fld.GetData(Bytes);
Output.WriteData(Bytes, Length(Bytes));
end;
// Do whatever with output
finally
Output.Free;
end;
end;

Delphi7. Read BLOB field with WideString data in it

Can anybody help? I'm keeping an old Delphi7 project and have next trouble. How can I store BLOB field value if it contains Unicode string?
I tried:
var
str: WideString;
begin
...
str := WideString(Fields[1].AsString); - but I get empty string
...
...
str := VarToWideStr(Fields[1].AsVariant); - but I get "(BLOB)" result in str varible.
...
end;
My solution:
Code usage:
...
stream := TMemoryStream.Create;
try
Fields[1].SaveToStream(stream);
ss := MemStreamToWStr(stream);
finally
stream.Destroy;
end;
...
And function:
function TSnsFrame.MemStreamToWStr(Mstream: TMemoryStream): WideString;
begin
Mstream.Seek(0, soFromBeginning);
SetLength(Result, Mstream.size div 2);
MStream.ReadBuffer(Result[1], Mstream.size);
end;
Have a look at TDataSet.CreateBlobStream(). It returns a TStream that can be used to read/write the raw data of a blob field.

Resources