I have created a small program the read a text file.
Once the text file is opened in a RichEdit, I want to change the background color of lines that contain a certain string, or to hide all lines that do not contain the string. Is it possible?
I have tried to search for the string, but I haven't any idea of how to do what I'm asking for.
function SearchText(Control: TCustomEdit; Search: string; SearchOptions: TSearchOptions): Boolean;
var
Text: string;
Index: Integer;
begin
if soIgnoreCase in SearchOptions then
begin
Search := UpperCase(Search);
Text := UpperCase(Control.Text);
end
else
Text := Control.Text;
Index := 0;
if not (soFromStart in SearchOptions) then
Index := PosEx(Search, Text, Control.SelStart Control.SelLength 1);
if (Index = 0) and
((soFromStart in SearchOptions) or
(soWrap in SearchOptions)) then
Index := PosEx(Search, Text, 1);
Result := Index > 0;
if Result then
begin
Control.SelStart := Index - 1;
Control.SelLength := Length(Search);
end;
end;
CodePudding user response:
Searching and filtering text before putting it into the RichEdit is best.
However, if the text is already loaded in the RichEdit, TRichEdit does have a FindText() method you can use, you should not be searching its Text property manually. For example:
function SearchText(Control: TCustomRichEdit; const Search: string; SearchOptions: TSearchOptions): Boolean;
var
StartPos, SearchLen, Index: Integer;
Options: TSearchTypes;
begin
if soIgnoreCase in SearchOptions then
Options := []
else
Options := [stMatchCase];
if soFromStart in SearchOptions then
begin
StartPos := 0;
SearchLen := Control.GetTextLen;
Index := Control.FindText(Search, StartPos, SearchLen, Options);
end else
begin
StartPos := Control.SelStart Control.SelLength;
SearchLen := Control.GetTextLen - StartPos;
Index := Control.FindText(Search, StartPos, SearchLen, Options);
if (Index = -1) and (soWrap in SearchOptions) then
Index := Control.FindText(Search, 0, StartPos, Options);
end;
Result := Index <> -1;
if Result then
begin
Control.SelStart := Index;
Control.SelLength := Length(Search);
end;
end;
That being said, setting the background color of a line, or removing a line (there is no option to "hide" a line), is fairly simple.
Given any character index, you can send the RichEdit an EM_LINEFROMCHAR message to determine the line index that the character appears on.
You can then remove the line from the RichEdit by using the TRichEdit.Lines.Delete() method.
To set the line's background color takes a few steps:
send the RichEdit
EM_LINEINDEXandEM_LINELENGTHmessages to determine the line's starting and ending character indexes.set the RichEdit's
SelStartandSelLengthproperties (or send the RichEdit anEM_EXSETSELmessage).send the RichEdit an
EM_SETCHARFORMATmessage, specifying the SCF_SELECTION flag and using theCHARFORMAT2record, to set the background color of the selection.
CodePudding user response:
This is kind of a poor SO question, because it is a bit like "please write the code for me".
The natural approach would be to find the independent parts of the problem:
How to represent an array of strings (lines) in Delphi?
How to load a text file in Delphi into some in-memory array of strings?
How to search for a substring in a string in Delphi?
How to filter a Delphi in-memory array of strings? [This is trivial if you know 1 and has heard of loops. Doing it efficiently is slightly more interesting.]
How to populate a Delphi
TRichEditcontrol?
Indeed, if you know the answers to 1--5, doing what you want is trivial!
I might seem like a very grumpy old man now, but I think I do have a very important point about how to approach a programming problem.
Anyhow, let us address one issue at a time:
The old-school approach of a
array of string, today writtenTArray<string>works. This is a dynamic array of strings. Since Delphi dynamic arrays are managed by the compiler, they are convenient because you don't need to create and free them manually. However, they are a bit low-level and are sometimes misused.Probably a better alternative for you is to use the
TStringListclass.In
IOUtils, you findTFile.ReadAllLineswhich takes a file name and returns the contents of the (text) file as an array of strings.Or use
TStringList.LoadFromFileif you have aTStringList.Traditionally, you would use the
Posfunction. But today you can use thestringhelper:MyString.Contains(). Obviously, you need to decide if you want to treat CAPITALS and small letters as identical or not.Use a trivial
fororfor inloop to populate a second array from the original array, based on the test from 3.If you have a
TStringList, just useTRichEdit.Lines.Assign.
Putting it all together, using a fairly smart combination of string arrays and TStringList:
procedure TForm1.Button1Click(Sender: TObject);
begin
var Lines := TFile.ReadAllLines('K:\test.txt');
var FilteredLines := TStringList.Create;
try
for var Line in Lines do
if Line.Contains('MyString') then
FilteredLines.Add(Line);
RichEdit1.Lines.Assign(FilteredLines);
finally
FilteredLines.Free;
end;
end;
