Lazarus

Free Pascal => Beginners => Topic started by: justnewbie on April 16, 2019, 02:11:03 pm

Title: Position in a Memo
Post by: justnewbie on April 16, 2019, 02:11:03 pm
Hi,
I have a text in a Memo.
I'm scanning the Memo line by line then character by character like this:

Code: Pascal  [Select][+][-]
  1. for i := 0 to Memo1.Lines.Count - 1 do
  2.   begin
  3.     j := Pos('#', Memo1.Lines[i]);  
  4.     if (j <> 0) then  //The given line contains the '#'
  5.     begin      
  6.       for k := Length(Memo1.Lines[i]) downto 1 do  
  7.          begin
  8.             if Memo1.Lines[i][k] = '#' then  //Get the position of '#' in the line
  9.             ...

Now I know the position of '#' character in the line (say 14), but I too need to know which is this position in the whole Memo text (say 352)?
How can I get the 352?
Thank you
Title: Re: Position in a Memo
Post by: ASerge on April 16, 2019, 02:26:00 pm
Now I know the position of '#' character in the line (say 14), but I too need to know which is this position in the whole Memo text (say 352)?
How can I get the 352?
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   iLine, iPos: SizeInt;
  4. begin
  5.   for iLine := 0 to Memo1.Lines.Count - 1 do
  6.   begin
  7.     iPos := Pos('#', Memo1.Lines[iLine]);
  8.     if iPos <> 0 then
  9.     begin
  10.       Memo1.SelLength := 0;
  11.       Memo1.CaretPos := Point(iPos - 1, iLine);
  12.       Caption := IntToStr(Memo1.SelStart);
  13.       Break;
  14.     end;
  15.   end;
  16. end;
Title: Re: Position in a Memo
Post by: Birger52 on April 16, 2019, 05:03:09 pm
From
http://wiki.lazarus.freepascal.org/TMemo

Code: Pascal  [Select][+][-]
  1. function FindInMemo(AMemo: TMemo; AString: String; StartPos: Integer): Integer;
  2. begin
  3.   Result := PosEx(AString, AMemo.Text, StartPos);
  4.   if Result > 0 then
  5.   begin
  6.     AMemo.SelStart := UTF8Length(PChar(AMemo.Text), Result - 1);
  7.     AMemo.SelLength := Length(AString);
  8.     AMemo.SetFocus;
  9.   end;
  10. end;

So ...
PositionOf# := Pos('#', Memo1.Text);
Should do the trick...
Title: Re: Position in a Memo
Post by: justnewbie on April 16, 2019, 05:15:56 pm
Thank you guys!

@ASerge:
It is what I need, works well!  :)

@Birger52:
I knew that, but it is not good for me, because there can be more than 1 '#' in the text.
Title: Re: Position in a Memo
Post by: justnewbie on April 17, 2019, 09:00:31 pm
New question guys (didn't want to open a new thread).

I'd like to open a binary file (*.exe) in a Memo.
It only gives some characters from the file and that's it.
How can I get it to work like in the Notepad?
Is it possible by using a Memo?
See my pictures for illustration.
Title: Re: Position in a Memo
Post by: jamie on April 17, 2019, 10:58:03 pm
Note pad does not show everything ether..

in any case, you need to make yourself a debugger view of the file that shows the HEX on the left in formatted state and
Ansi on the right. All non-ansi letters to be shown as DOTS or ? marks.

 Use a MONO font so that things align.

 you can use A TMemoryStream to load the complete contents and then generate a HEX dump text file of it.

 I am sure there are LIBS already out there you can compile in to do that for you..

Title: Re: Position in a Memo
Post by: justnewbie on April 17, 2019, 11:38:14 pm
Note pad does not show everything ether..

in any case, you need to make yourself a debugger view of the file that shows the HEX on the left in formatted state and
Ansi on the right. All non-ansi letters to be shown as DOTS or ? marks.

 Use a MONO font so that things align.

 you can use A TMemoryStream to load the complete contents and then generate a HEX dump text file of it.

 I am sure there are LIBS already out there you can compile in to do that for you..
I am not sure that I understand you exactly, maybe you went further than what I need.
At this point I only need to display the binary in a Memo,  but it seems it is not possible.
Title: Re: Position in a Memo
Post by: lucamar on April 18, 2019, 02:28:00 pm
It's possible, but you can't use LoadFromFile(); instead you have to load the binary outside TMemo, p.e. in a TMemoryStream or even in a String (with FileUtil.ReadFileToString()) and replace all problematic characters with whatever you want: a space, a dot, etc. Then you can load that into the TMemo either by using LoadFromStream, if you used a memory stream, or setting TMemo.Text
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 03:39:31 pm
It's possible, but you can't use LoadFromFile(); instead you have to load the binary outside TMemo, p.e. in a TMemoryStream or even in a String (with FileUtil.ReadFileToString()) and replace all problematic characters with whatever you want: a space, a dot, etc. Then you can load that into the TMemo either by using LoadFromStream, if you used a memory stream, or setting TMemo.Text
Thank you, I will give it a go and let's see.

Edit: which ones can be the "problematic characters", that should be changed?
Title: Re: Position in a Memo
Post by: lucamar on April 18, 2019, 04:27:48 pm
Edit: which ones can be the "problematic characters", that should be changed?

All the ASCII control chars, the set:
  [#00..#$1F]
Or if you want to see the effect of the line ending chars:
  [#00..#$09, #$0B, #$0C, #$0E..#$1F]
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 04:44:23 pm
Thank you! So these are the 0-31 characters in ANSI table.
Is it possible to change a character directly within the MemoryStream, or should I use a string for it?
Title: Re: Position in a Memo
Post by: lucamar on April 18, 2019, 05:11:30 pm
Is it possible to change a character directly within the MemoryStream, or should I use a string for it?

It can be done but it's easier if you use a TByteStream: it has a property Bytes that allows you to access the stream data as an array of bytes.

Or, of course, you could also use a TStringStream. :)

But IMO the easiest way would be something like this:
Code: Pascal  [Select][+][-]
  1. uses FileUtil;
  2.  
  3. const
  4.   ProblemChars = [#0..#31];
  5.   Space = #32;
  6.  
  7. procedure ReadBinaryToMemo(const AFilename: String; const Memo: TMemo);
  8. var
  9.   AString: RawByteString;
  10.   i; Integer;
  11. begin
  12.   AString := ReadFileToString(AFilename);
  13.   if AString = '' then
  14.     Memo.Clear
  15.   else begin
  16.     for i := 1 to Length(AString) do
  17.       if AString[i] in ProblemChars then
  18.         AString[i] := Space;
  19.     Memo.Text := AString;
  20.   end;
  21. end;
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 05:44:00 pm
Thank you, I will try it soon!

Also, can you tell me why is this code bad?
(Wanted to change the "problematic characters" to a dot, but finally I got an empty memo.)

Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   AStr: string;
  4.   i: integer;  
  5. begin
  6.   AStr := FileUtil.ReadFileToString('notepad.exe');
  7.   for i := 0 to 31 do   // 32 ANSI control characters
  8.     begin
  9.       while Pos(chr(i), AStr) <> 0 do
  10.       begin
  11.         AStr := StuffString(AStr, Pos(chr(i), AStr), 1, '.');
  12.       end;
  13.     end;
  14.  
  15.   Memo1.Lines.Add(AStr);  
  16. end;  
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 06:06:23 pm
Tried your code, but it somehow didn't work.
This way:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   StrUtils;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Memo1: TMemo;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure ReadBinaryToMemo(const AFilename: String; const Memo: TMemo);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32.  
  33. { TForm1 }
  34.  
  35. procedure TForm1.Button1Click(Sender: TObject);
  36. begin
  37.   ReadBinaryToMemo('notepad.exe', Memo1);
  38. end;
  39.  
  40. procedure TForm1.ReadBinaryToMemo(const AFilename: String; const Memo: TMemo);
  41. const
  42.   ProblemChars = [#0..#31];
  43.   Space = #32;
  44. var
  45.   AString: RawByteString;
  46.   i: integer;
  47. begin
  48.   AString := ReadFileToString(AFilename);
  49.   if AString = '' then
  50.     Memo.Clear
  51.   else begin
  52.     for i := 1 to Length(AString) do
  53.       if AString[i] in ProblemChars then
  54.         AString[i] := Space;
  55.     Memo.Text := AString;
  56.   end;
  57. end;
  58.  
  59. end.        
         
Title: Re: Position in a Memo
Post by: lucamar on April 18, 2019, 06:40:44 pm
"didn't work" tells me nothing of what it did/didn't do. Please be a little more specific.

Now just guessing but, do you have "notepad.exe" in your program directory? If not, you must provide the full path to the file for it to be found.

Replace Button1Click() with this after adding an OpenDialog to the form:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   if OpenDialog1.Execute then
  4.     ReadBinaryToMemo(OpenDialog1.Filename, Memo1);
  5. end;

If it doesn't work, explain carefully what it does/doesn't. But it *should* work: at least it does here.

Do note that I left out all the common checks: you should add them yourself: check that the file exists, that the memo is assigned, etc.
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 07:17:38 pm
Yes, of course, the exe file is in the program's directory.
Memo1 is on the form.
Did not work: I got an empty Memo.
Title: Re: Position in a Memo
Post by: lucamar on April 18, 2019, 08:05:09 pm
Try this for testing:
Code: Pascal  [Select][+][-]
  1. procedure ReadBinaryToMemo(const AFilename: String; const Memo: TMemo);
  2. var
  3.   AString: RawByteString;
  4.   i; Integer;
  5. begin
  6.   if AFilename.IsEmpty then
  7.     ShowMessage('AFilename is empty!')
  8.   else if not FileExists(AFileName) then
  9.     ShowMessageFmt('File: %s doesn''t exits!', [AFilename])
  10.   else begin
  11.     AString := ReadFileToString(AFilename);
  12.     if AString = '' then begin
  13.       ShowMessage('The file is empty?!')
  14.     end else begin
  15.       for i := 1 to Length(AString) do
  16.         if AString[i] in ProblemChars then
  17.           AString[i] := Space;
  18.       Memo.Text := AString;
  19.     end;
  20.   end;
  21. end;

and tell me if any of the mesage boxes is shown.
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 08:28:55 pm
Extremely strange. Nothing happens. No message at all and the Memo is empty.
I have a form and a Button and a Memo on it.
I used this code:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   StrUtils;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Button1: TButton;
  17.     Memo1: TMemo;
  18.     procedure Button1Click(Sender: TObject);
  19.     procedure ReadBinaryToMemo(const AFilename: String; const Memo: TMemo);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. {$R *.lfm}
  33.  
  34. { TForm1 }
  35.  
  36.  
  37. procedure TForm1.Button1Click(Sender: TObject);
  38. begin
  39.   ReadBinaryToMemo('notepad.exe', Memo1);
  40. end;
  41.  
  42. procedure TForm1.ReadBinaryToMemo(const AFilename: String; const Memo: TMemo);
  43. const
  44.   ProblemChars = [#0..#31];
  45.   Space = #32;
  46. var
  47.   AString: RawByteString;
  48.   i: Integer;
  49. begin
  50.   if AFilename.IsEmpty then
  51.     ShowMessage('AFilename is empty!')
  52.   else if not FileExists(AFileName) then
  53.     ShowMessageFmt('File: %s doesn''t exits!', [AFilename])
  54.   else begin
  55.     AString := ReadFileToString(AFilename);
  56.     if AString = '' then begin
  57.       ShowMessage('The file is empty?!')
  58.     end else begin
  59.       for i := 1 to Length(AString) do
  60.         if AString[i] in ProblemChars then
  61.           AString[i] := Space;
  62.       Memo.Text := AString;
  63.     end;
  64.   end;
  65. end;
  66.  
  67. end.

Obviously I did something wrong, but cannot find.
It seems that "ReadBinaryToMemo" doesn't run when I click the button?  :(
Title: Re: Position in a Memo
Post by: lucamar on April 18, 2019, 08:54:14 pm
Extremely strange. Nothing happens. No message at all and the Memo is empty.
I have a form and a Button and a Memo on it.
I used this code:
Code: Pascal  [Select][+][-]
  1. {... code here ...}

Obviously I did something wrong, but cannot find.

You did nothing wrong and you're right: it doesn't work, although I don't know why. It worked before in a simpler CLI test (which unfortunately I've deleted :( ).

Let me try a couple things and let's see if I can make it work somehow.
Title: Re: Position in a Memo
Post by: justnewbie on April 18, 2019, 09:07:34 pm
OK, thank you for your effort!
(Maybe it has nothing to do with this thing, but I found earlier that the Memo component cannot load files that are in UTF-16 format, only with UTF-8. I don't know if it does matter in this case?)
Title: Re: Position in a Memo
Post by: lucamar on April 19, 2019, 12:06:44 am
Ok, it has been a couple funny (not!) hours of trying this and that and finally managed to ascertain that it's not the transposition that fails: that works well, one just have to save the result string to a file to see it.

What's failing is adding the string to the memo! Nothing works: Memo.Append(), Memo.Lines.Add(), Memo.Lines.AddText(), assigning Memo.Text, assigning Memo.Lines.Text, ... nothing!

And here is the even more curious thing: if the file constains only text, it works: the transposition simply replaces all line-endings with dots/spaces and the whole appears as a single line. But with a binary file, which in theory should do the same, nothing appears in the memo. >:D

I'm completely surprised, atonit, flaberglasted.

Only explanation I can think of (and is as wild a guess as they come) is that the memo is trying to interpret the string as UTF-8 and failing miserably.

Next test: After converting the string, save it to a temp file and open that file in the memo with LoadFromFile(). But I'll leave that to you ... ok, no: here it is, in all its untested "glory":
Code: Pascal  [Select][+][-]
  1. const
  2.   ProblemChars = [#0..#31];
  3.   Placeholder = '.';
  4.  
  5. procedure ReadBinaryToMemo(const AFilename: String; const AMemo: TMemo);
  6. var
  7.   AString: RawByteString;
  8.   i; Integer;
  9. begin
  10.   AString := ReadFileToString(AFilename);
  11.   if AString = '' then
  12.     ShowMessage('Seems to be an empty file?')
  13.   else begin
  14.     { Replace problematic chars by dots }
  15.     for i := 1 to Length(AString) do
  16.       if AString[i] in ProblemChars then
  17.         AString[i] := Placeholder;
  18.     { Save the converted string to a temp file }
  19.     if FileExists('memotest.temp') then
  20.       DeleteFile('memotest.temp');
  21.     with TFileStream.Create('memotest.temp', fmCreate) do
  22.     try
  23.       { Extremely simplistic, naïve approach }
  24.       if Write(AString[1], Length(AString)) <> Length(AString) then
  25.         ShowMessage('Didn''t write the full string :( ');
  26.     finally
  27.       Free;
  28.     end;
  29.     {and load that file into the memo}
  30.     try
  31.       AMemo.LoadFromFile('memotest.temp');
  32.     finally
  33.       DeleteFile('memotest.temp');
  34.     end;
  35.   end;
  36. end;

(Maybe it has nothing to do with this thing, but I found earlier that the Memo component cannot load files that are in UTF-16 format, only with UTF-8. I don't know if it does matter in this case?)

No, it doesn't matter in this case but ... are you sure of that? I kind of remember having opened UTF16 files w/out problems. Of course my memory may be at fault here; I don' use many UTF16 files. :)
Title: Re: Position in a Memo
Post by: ASBzone on April 19, 2019, 03:41:52 am
What's failing is adding the string to the memo! Nothing works: Memo.Append(), Memo.Lines.Add(), Memo.Lines.AddText(), assigning Memo.Text, assigning Memo.Lines.Text, ... nothing!

And here is the even more curious thing: if the file constains only text, it works: the transposition simply replaces all line-endings with dots/spaces and the whole appears as a single line. But with a binary file,


Does it have anything to do with data being flushed to disk?
Title: Re: Position in a Memo
Post by: lucamar on April 19, 2019, 03:34:16 pm
Does it have anything to do with data being flushed to disk?

No, I don't think so. Whatever you do with it the string already has he data (printabl data, at that) and the memo should accept it whithout more ado ... but it doesn't. It behaves as if the string where empty, which it isn't. A real to goodness mistery :)

Have you been able to test saving to a temp file and loading from the it? I'm mired in another project and can't dedicate to this the time it merits.
Title: Re: Position in a Memo
Post by: justnewbie on April 19, 2019, 10:45:38 pm
Hi lucamar, very sorry for not answering, but I cannot try it for 2 days. Will be back then and will look at it.
Title: Re: Position in a Memo
Post by: lucamar on April 21, 2019, 10:25:40 am
Hi lucamar, very sorry for not answering, but I cannot try it for 2 days. Will be back then and will look at it.

I have finally found some little time to try it and it doesn't work but I think it may be something with the file I'm "converting" here (the executable of the test itself) because I've been unable to open it with any other editor (and I have quite some of them installed: GEdit, Leafpad, Scribes, X2, ...)

If you manage to test it and it works tell me about it, will you? I'll keep testing when I can.
Title: Re: Position in a Memo
Post by: justnewbie on April 21, 2019, 11:18:48 am
Here again.
Tested, doesn't work.

Some remarks (I am on Linux).
The 'notepad.exe' cannot be opened by the usual text editors (xed, medit ...).
But, it can be opened in the Notepad (running via Wine).
Other thing:
As wrote earlier, I found that UTF-16 encoded texts cannot be loaded into the TMemo component by the LoadFromFile. (Why?)
I have to convert/save it to UTF-8 in an external text editor and then Memo will accept it.
Also, if I copy/paste a UTF-16 text directly into the Memo, the text will appear in the Memo flawlessy.
Is it a bug or is it normal?

Title: Re: Position in a Memo
Post by: lucamar on April 21, 2019, 12:12:38 pm
Here again.
Tested, doesn't work.

Some remarks (I am on Linux).
The 'notepad.exe' cannot be opened by the usual text editors (xed, medit ...).
But, it can be opened in the Notepad (running via Wine).

I must confess I didn't try that. My Wine installation has some peculiar ideas as to what constitutes a good font size which I don't share at all :)

Quote
As wrote earlier, I found that UTF-16 encoded texts cannot be loaded into the TMemo component by the LoadFromFile. (Why?)
I have to convert/save it to UTF-8 in an external text editor and then Memo will accept it.
Also, if I copy/paste a UTF-16 text directly into the Memo, the text will appear in the Memo flawlessy.
Is it a bug or is it normal?

It may be normal: TMemo is a very basic editor (basically a multiline TEdit) and may not be ready to accept UTF16 text. Pasting from the clipboard is different: IIRC, the clipboard itself does the change.

By the way, you don't need to convert/save in an external editor: you can load your file to an UnicodeString and convert it to UTF-8 in your program, and viceversa for saving. Somewhat wasteful of memory but that that doesn't seem to matter much nowadays.

Of course, it means writing your own routines to Load/Save files but that is not very difficult: you could even write a type helper for TMemo with them :)
TinyPortal © 2005-2018