Delphi memo lines in one line - delphi

In a TMemo field I have 3 lines:
line1
line2
line3
Is it posible to get all three lines as one string?
Example:
line1,line2,line3

You can use the Lines.CommaText property for this. Do the following:
CommaString := Memo1.Lines.CommaText;
Its also useful to use the DelimitedText property if you want the text to make use of another separator character. You can do that by using something like this:
Memo1.Lines.Delimiter := '-';
Memo1.Lines.StrictDelimiter := True;
DashString := Memo1.Lines.DelimitedText;
This works both ways. You can assign a value to the CommaText or DelimiterText to set the lines. This is actually a of TStringList so it will work with TListBox, TMemo, TComboBox, etc. Basically anything that uses a string list internally.

maybe something like this suits your needs
d:=memo1.lines.count;
for i:=1 to d do
memo1.lines[0]:=memo1.lines[0]+' '+memo1.lines[i];
for i:=1 to d do
memo1.lines.Delete(1);

here is a 3 line function that do it.
function getOneLineMemo(memo:Tmemo):String;
var
i:integer;
begin
result := '';
for i:=0 to memo1.lines.count do
result := result + memo.lines[0];
end;

Related

Search database table by record fields and display record on cxgrid

What is the best way to allow a user to search for a record in a database table by typing text into a tedit box clicking a button then the results will then display onto a tcxgrid.
I have a tadquery/datasource that is looking at a table which contains various fields. It would be nice if the user was able to search for more than one field in the table.
Thanks.
You can use TDataSet.Locate for this, passing a semicolon delimited list of field names and an array of constant field values to match. Typically, this is easy:
DataSet.Locate('Field1;Field2', ['Value1', 'Value2'], [loPartialKey]);
However, as you don't know ahead of time how many columns, you'll need to handle the array differently, using VarArrayCreate and setting each array value separately.
This example takes the list of fields from Edit1 (separated by semicolon), the list of values to match from Edit2 (again, separated by semicolons, with string values surrounded by ' characters so they're properly included in the array). It splits the content of Edit1 into an array to find out how many elements, allocates a variant array of the proper size, populates it, and then passes both the field list and the array of values to TDataSet.Locate. It was tested with Edit1.Text := 'Customer_No;Name'; and Edit2.Text := '1;''Smith''';.
procedure TForm5.Button1Click(Sender: TObject);
var
Temp: string;
Fields: TArray<string>;
Vals: TArray<string>;
FieldValues: Variant;
i: Integer;
begin
// Grab a copy so we can split it into separate values
Temp := Edit1.Text;
Fields := Temp.Split([';']);
// Create the array of variants to hold the field values
FieldValues := VarArrayCreate([0, High(Fields)], VarVariant);
// Temporary copy to allow splitting into individual values
Temp := Edit2.Text;
Vals := Temp.Split([';']);
for i := 0 to High(Fields) do
FieldValues[i] := Vals[i];
// Use original field list from Edit1 for the Locate operation
DataSet1.Locate(Edit1.Text, FieldValues, [loCaseInsensitive]);
end;
For versions of Delphi prior to the inclusion of TStringHelper (such as the XE2 you're using, just add Types and StrUtils to your uses clause and use SplitString and TStringDynArray instead:
procedure TForm5.Button1Click(Sender: TObject);
var
Temp: string;
Fields: TStringDynArray;
Vals: TStringDynArray;
FieldValues: Variant;
i: Integer;
begin
Temp := Edit1.Text;
Fields := SplitString(Temp, ';');
FieldValues := VarArrayCreate([0, Length(Fields)], VarVariant);
Temp := Edit2.Text;
Vals := SplitString(Temp, ';');
for i := 0 to High(Fields) do
FieldValues[i] := Vals[i];
DataSet1.Locate(Temp, FieldValues, [loCaseInsensitive]);
end;
I would use a query for the datasource and in the onclick event of the button, reload the query with a WHERE clause along the lines of
Query1.SQL.Add('WHERE Name LIKE :P1 OR Postcode LIKE :P2 OR Town LIKE :P3');
and add the parameters
Query1.SQL.Parameters.ParamByName('P1').Value := '%' + Edit1.Text + '%';
Query1.SQL.Parameters.ParamByName('P2').Value := '%' + Edit1.Text + '%';
Query1.SQL.Parameters.ParamByName('P3').Value := '%' + Edit1.Text + '%';
using '%' to allow searching anywhere in the string as an option.
Query1.Open;
I've used this technique many times.

Indexes order in Delphi?

I'm basically coding some sort of table where for column tags I have some numbers and for row tags I have some strings which contain such numbers separated by commas.
I'm taking all the row tags from a TString named minterms_essentials and the column tags from one named minterms.
First I must tag the created 2 dimensions array. And then, if any string from the rows contains certain letter, I must place an 'x' in the proper column.
I've wrote this Delphi code but I'm getting access violation so far...
SetLength(tabla, minterms_essentials.Count+1,minterms.Count+1);
for i := 0 to minterms.Count-1 do
begin
tabla[0,i+1] := IntToStr(BinToInt(minterms[i]));
end;
for i := 0 to minterms_essentials.Count-1 do
begin
tabla[i+1,0] := minterms_essentials[i];
end;
for i := 1 to minterms_essentials.Count-1 do
begin
for g := 1 to minterms.Count-1 do
begin
ss := tabla[g,0].Split([',']);
for s in ss do
begin
if s = tabla[0,g] then
begin
tabla[g,i] := 'x';
end;
end;
end;
end;
Is there any better and correct way to do this?
Look at this:
first dimension is defined by minterms_essentials
SetLength(tabla, minterms_essentials.Count+1,minterms.Count+1);
but here you are using minterms to index first dimension of array:
for g := 1 to minterms.Count-1 do
begin
ss := tabla[g,0].Split([',']);
P.S. Have you still not turned on range check?

Search TextFile for Line with a specific string

Is it possible to search a TextFile line by line to find a specific string.
ie. Hello
It searches line by line to find if any line has Hello in it. There will only be one string per line.
Is this possible?
If so how do I attempt this?
It's certainly easiest to load the entire file into memory. Provided that your file is small enough then you can do it like this:
found := false;
sl := TStringList.Create;
try
sl.LoadFromFile(fileName);
for line in sl do
if Pos('Hello', line)<>0 then
begin
found := true;
break;
end;
finally
sl.Free;
end;
I assume that when you say
if any line has Hello in it
that you are looking for lines that contain the search string rather than lines that equal the search string.
In a comment you ask:
Ok can I then ask if it would be possible to ask on how to
delete a string that is typed from an edit box out of a file? So
you enter a string into an edit box then it searches the file
for it and deletes that line?
That's an easy enough variation of the above:
procedure RemoveLinesContaining(const fileName, searchText: string);
var
sl: TStringList;
i: Integer;
begin
sl := TStringList.Create;
try
sl.LoadFromFile(fileName);
for i := sl.Count-1 downto 0 do
if Pos(searchText, sl[i])<>0 then
sl.Delete(i);
sl.SaveToFile(fileName);
finally
sl.Free;
end;
end;
This function deletes all lines that contain the search string. If you only want to delete the first such line, then break out of the for loop after the call to Delete.
The loop variable is descending to allow the loop to modify the list.
If you want to use a different test, say equality rather than contains, then simply modify the if statement.
An easy way is to use TStringList.LoadFromFile to load the file, then check IndexOf('hello') - if it's greater than -1, the string is in the file.
var
sl : TStringList;
ix : Integer;
begin
sl := TStringList.Create;
try
sl.LoadFromFile('test.txt');
ix := sl.IndexOf('Hello');
if ix > -1 then ShowMessage('Yup, the file contains a greeting.');
finally
sl.Free;
end;
end;

Listing Filenames from a Directory with sorting? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How to get the sort order in Delphi as in Windows Explorer?
I am trying to scan a directory, but I can't get it to Sort by File Name.
Example, say if I have these Filenames in a folder:
File1
File2
File3
File4
File5
File6
File7
File8
File9
File10
File11
File12
File13
File14
File15
File16
File17
File18
File19
File20
File21
File22
If I use something like this:
var
SL: TStringList;
SR: TSearchRec;
begin
SL := TStringList.Create;
try
if FindFirst(Path + '*.*', faAnyFile and not faDirectory and not faHidden, SR) = 0 then
repeat
SL.Add(Path + SR.Name)
until FindNext(SR) <> 0;
FindClose(SR);
// handle the filenames..
finally
SL.Free;
end;
end;
The result will be:
File10
File11
File12
File13
File14
File15
File16
File17
File18
File19
File2
File20
File21
File22
File3
File4
File5
File6
File7
File8
File9
It should be sorted by Filename (as I wrote in the first Filename list example).
I bet this is really simple but I cannot see it, what do I need to do to sort this?
Thanks.
You're starting with the assumption that there's some sort of inherent "order" for file names. There isn't. You appear to want the file names to be sorted alphabetically, with numerical portions of names sorted numerically. I'm not sure what you want to happen with punctuation and other characters.
The file-enumeration functions don't define any order that names will be returned in. They're returned in whatever order the underlying file system decides to provide them. There are two steps to getting a sorted list of file names. You're already doing the first one:
Collect the file names in a list for post-processing.
Arrange the names in the order you want. If plain "asciibetical" isn't what you want, then you can write a custom sorting function and pass it to TStringList.CustomSort.
For example, if you want them to be in the same order you see file names in Windows Explorer as of Windows XP, you can use the StrCmpLogicalW API function. Call that from your comparison function, like this:
function LogicalCompare(List: TStringList; Index1, Index2: Integer): Integer;
begin
Result := StrCmpLogicalW(PWideChar(List[Index1]), PWideChar(List[Index2]));
end;
SL.CustomSort(LogicalCompare);
If you have something earlier than Delphi 2007, you'll need to do something about converting your strings to wide characters, at least for the duration of the sorting phase.
FindFirst() and FindNext() merely enumerate the files on the file system as-is. The files can be returned in any order. You have to sort the TStringList yourself afterwards, eg:
function SortFilesByName(List: TStringList; Index1, Index2: Integer): Integer;
var
FileName1, FileName2: String;
FileNumber1, FileNumber2: Integer;
begin
// assuming the files are all named "Path\File###.xxx",
// where "###" is the number to sort on...
FileName1 := ChangeFileExt(ExtractFileName(List[Index1]), '');
FileName2 := ChangeFileExt(ExtractFileName(List[Index1]), '');
FileNumber1 := StrToInt(Copy(FileName1, 5, MaxInt));
FileNumber2 := StrToInt(Copy(FileName2, 5, MaxInt));
Result := (FileNumber2 - FileNumber1);
end;
var
SL: TStringList;
SR: TSearchRec;
begin
SL := TStringList.Create;
try
if FindFirst(Path + '*.*', faAnyFile and (not faDirectory) and (not faHidden), SR) = 0 then
try
repeat
SL.Add(Path + SR.Name)
until FindNext(SR) <> 0;
finally
FindClose(SR);
end;
SL.CustomSort(SortFilesByName);
// handle the filenames..
finally
SL.Free;
end;
end;
Any simple sort system (such as the one windows uses to return you files and delphi uses for sorting) will sort alphabetically and then by number, but unless you pad your numbers with zeros
1 comes before 2 so
11 comes before 2 (in the same way that aa comes before b)
you either need to pad you numbers with zeros such as
filename001
filename010
filename020
for use the answer provided by Remy Lebeau - TeamB above which will pull out the number at the end of you filename and sort by that.

Delphi label values sorting

Im trying to sort Label's values. I have lots of labels with an integer value. Labels are called like Label1, Label2, [...], which Im accessing through FindComponent. I have no problem in sorting the integer values Ive stored in an array, but the problem is, after sorting, I have no idea which label had what value. My goal is to like, sort those labels by their value, so I'd get like an array with Labels sorted by their value. Im stuck at this point :(
Eg:
Label1.Caption := 10;
Label2.Caption := 4;
Label3.Caption := 7;
for i := 1 to 3
do some_array[i] := StrToInt(TLabel(FindComponent('Label' + IntToStr(i))).Caption);
sortarray(some_array);
Now, I have sorted array, but Im lacking some sort procedure that would also store label number in the corresponding place. Can someone point me out?
Instead of creating an array of integers, create an array of TLabel controls. This one you can sort the same way as the array of integers. Indeed, given a MyLabel: TLabel, you can easily get the associated integer as StrToInt(MyLabel.Caption).
In addition, the FindComponent approach is not very efficient. I'd do
const
ALLOC_BY = 100;
MAGIC_TAG = 871226;
var
i: Integer;
ActualLength: integer;
FLabels: array of TLabel;
begin
SetLength(FLabels, ALLOC_BY);
ActualLength := 0;
for i := 0 to ControlCount - 1 do
if Controls[i] is TLabel then
with TLabel(Controls[i]) do
if Tag = MAGIC_TAG then
begin
if ActualLength = length(FLabels) then
SetLength(FLabels, length(FLabels) + ALLOC_BY);
FLabels[ActualLength] := Controls[i];
inc(ActualLength);
end;
SetLength(FLabels, ActualLength);
SortArray(FLabels) // with respect to the StrToInt(CurLabel.Caption) of each
// CurLabel: TLabel.
Of course, you can skip the chunk allocating if you know the number of labels in advance.
Make sure that each of the labels that are to be included in the array have the Tag set to MAGIC_TAG.
Another option would be to create an array
FLabelDataArray: array of TLabelData;
of
type
TLabelData = record
Control: TLabel;
Value: integer;
end;
where
FLabelDataArray[i].Value := StrToInt(FLabelDataArray[i].Control.Caption);
is computed only once.
A quick-n-dirty solution that also works in old Delphi versions, is to use TStringList, which has a Sort method and an Objects property that allow you to associate one object to each entry in the list.
Note that the list is sorted in lexicographic order, so the integers must be left padded with zeroes when converted to strings.
var
list: TStringList;
i: integer;
lab: TLabel;
begin
Label1.Caption := '10';
Label2.Caption := '4';
Label3.Caption := '7';
list := TStringList.Create;
try
for i := 1 to 3 do begin
lab := TLabel(FindComponent('Label' + IntToStr(i)));
list.AddObject(Format('%10.10d', [StrToInt(lab.Caption)]), lab);
end;
list.Sort;
for i := 0 to list.Count-1 do
Memo1.Lines.Add(list[i] + #9 + TLabel(list.Objects[i]).Name);
finally
list.Free;
end;
end;
The output would be:
0000000004 Label2
0000000007 Label3
0000000010 Label1
Also, if instead of list.Sort you use list.Sorted := true, you get binary search on list as a bonus (using list.IndexOf or list.Find).
Edit: As Rudy says, visual components such as TLabel should only be used for displaying data, and not for storing and manipulating it. It is recommended to use appropiate data structures for this and to separate the logic of the program from its user interface.
As Andreas says, put the labels into a dynamic array, rather than sorting the values. Once you have them in such an array, sort them like this:
uses
Generics.Defaults, Generics.Collections;
procedure SortLabels(var Labels: array of TLabel);
var
Comparison: TComparison<TLabel>;
Comparer: IComparer<TLabel>;
begin
Comparison := function(const Left, Right: TLabel): Integer
begin
Result := StrToInt(Left.Caption)-StrToInt(Right.Caption);
end;
Comparer := TDelegatedComparer<TLabel>.Create(Comparison);
TArray.Sort<TLabel>(Labels, Comparer);
end;
As others have said, I don't think you're taking the right approach for this task. But, based on your question, a simple hack would be to use the tag property on each label to store its caption's value :
Label1.Caption := '10';
Label1.Tag:=10;
Label2.Caption := '4';
Label2.Tag:=4;
Label3.Caption := '7';
Label3.Tag := 7;
Then you can find the appropriate label by matching the 'array of integer' entry with the label's tag property.
Again: As Rudy and others have commented, your approach to this task is far from desirable, and my solution only conforms to your approach. The tag property itself is a hack built into Delphi, an artifact from ancient times, like 'label' and 'goTo' and really should not be used except out of sheer desperation - like when trying to re-work and retro-fit ancient, poorly written code or if you've got something in prod and you must get in a quick fix for an emergency situation.

Resources