Link to home
Start Free TrialLog in
Avatar of Wayne Barron
Wayne BarronFlag for United States of America

asked on

Search & Replace - Need to LOOP through. Code Supplied

(Open to a better suggestion)
I am looking through files with 5,000 - 100,00 Lines, so it need to be very Robust.

Is there a way to put this code into a LOOP?
I need it to continue searching through the opened document until it finds that very last item and replaced it.

Right now, it will only replace "1 word" at a time.

========================
procedure TCFF_Slammer.RearchAndReplace
           (InSearch, InReplace: string) ;
var X, ToEnd : integer;
    oldCursor : TCursor;
begin
   oldCursor := Screen.Cursor;
   Screen.Cursor := crHourglass;
   with RichEdit1 do
   begin
     X := 0;
     ToEnd := length(Text) ;
     X := FindText(inSearch, X, ToEnd, []) ;
     while X <> -1 do
     begin
       SetFocus;
       SelStart := X;
       SelLength := length(inSearch) ;
       SelText := InReplace;
       X := FindText(inSearch,
                     X + length(InReplace),
                     ToEnd, []) ;
     end;
   end;
   Screen.Cursor := oldCursor;
end;

procedure TCFF_Slammer.OButton6Click(Sender: TObject);
var
   SearchText, ReplaceText: string;
begin
   SearchText := ''+RichEdit2.text+'';  // Search using small [73h x 169w] RichEdit's instead of TEdits.
   ReplaceText := ''+RichEdit3.text+''; // Search using small [73h x 169w] RichEdit's instead of TEdits.
   RearchAndReplace(SearchText, ReplaceText) ;
   RichEdit1.Perform(EM_SCROLL,SB_LINEDOWN,0);
end;
=================

Thanks All;
(Will add more points if needed)
Avatar of flasht
flasht

Use STR_REPLACE instead :)
Oh sorry... string_replace();
Avatar of Wayne Barron

ASKER

Hello flasht

Where would I put [STR_REPLACE] at?

Or is it totally different then what I am doing here?

Thanks
StringReplace replaces all occurances of string in text...

Usage:

StringReplace(richedit.text,what_to_replace,to_what_to_replace,[rfReplaceAll]);
I tried it as a single code. (By itself)
And nothing? Did not replace nothing in the file?
My bad...
RichEdit.Text := StringReplace(richedit.text,what_to_replace,to_what_to_replace,[rfReplaceAll]);
Sorry for mistakes but im at work and dont have any place to test it. But it will work for sure now.
Sorry, still nothing??

This is how I have it writtein. (Using 2 TRichEdits in place of TEdits)

RichEdit1.Text := StringReplace(richedit1.text,'+ RichEdit2.Text +','+ RichEdit3.Text +',[rfReplaceAll]);

Tried this and still nothing.
Now, this "Is" suppose to be by itself correct?
Within no other code involved?

Thanks for your assistance.
Wayne
Its multiline text? The one your searching and replacing?
Yes.
There is over 5,000+ lines in the file that I am trying to work with now.
I need to be able to literate through the file and change all accurances of certain
Text Characters.

It is not a single line.
These are from a Product Datafeed.txt file.
Yes but I am asking about Replace String and To Replace String...
Richedit2 and 3 are not multiline, use trim():

RichEdit1.Text := StringReplace(richedit1.text,trim(RichEdit2.Text),trim(RichEdit3.Text),[rfReplaceAll]);

As far as i know it should work with multilne strings too...
RichEdit1.Text := StringReplace(richedit1.text,'+ '+trim(RichEdit2.Text)+'+','+ '+trim(RichEdit3.Text)+'+',[rfReplaceAll]);
Something like that with those '+' ... dunno what do u mean by them exactly... short example of data would be great (text,what to replace, to what to replace).
Ok... ive tested it on my computer through RC and it works... if you want a code, here you go sample project (only soruces):
http://theweed.org/code035.zip
The code that you supplied works:

RichEdit1.Text := StringReplace(richedit1.text,trim(RichEdit2.Text),trim(RichEdit3.Text),[rfReplaceAll]);

Do you know of a way to speed it up?
What I mean, is is there a way to go through all the lines faster?

Example
Character Count for "1" line = 2,699 Characters.

To replace up to 2-words per line, took about 3-minutes.

I need this to be as Robust as possible.
SOLUTION
Avatar of flasht
flasht

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Generally operating on non-visual structures is much faster...
The TStringList
took the same amount of time as the 1-line code did.
3-Minutes on the nose.

Anything else you can think of?
Not really... :(
Maybe try some components like this:
http://jansfreeware.com/janreplace.zip
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Russell.
Is there an Example of how to use the [FastStrings] Code?
I tried hunting for the [Delphi FastCode project] as it has the [FastStrings]
Implemented in it for the [Replace], but cannot find it on.
SourceForge states that there are no files to download.
Any information on this component would be great, as it seems to be exactly what I am hunting for.


flasht.
I have used Jans Components before, and the JansReplace is not that much faster then
The basic Windows Replace.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That is perfect Russell.

I up'd the points so that I can give "flasht" some good points as well.
As he tried his best to assist me.

Once again Russell, you have come through with some great information and components
And you flasht as well.

Take Care both of you.
Wayne
Hello Russell;

Would you have an idea as to making this code work with "TListBox's"?

Instead of having the information inserted into a TRichEdit2 & TRichEdit3, instead have it loaded from a file into a TListBox1 & TListBox2.

Something like this:

ListBox1 - Has a loaded list of Incorrect items.
Items := "     |

ListBox2 - Has a loaded list of Correct items.
Items := '     ","

OK, When the button is pressed it will then replace everything in the RichEdit1 (That is in the ListBox1)
With the information that is in the ListBox2.
Line for Line.

First Character in ListBox1   [Replaced By:]   First Character in ListBox2.  2nd, 3rd and so on...

I tried a test and of which does compile, does not work, which I was expected it not to work anyway.

RichEdit1.Text:=FastReplace(RichEdit1.Text, trim(ListBox1.Items.Text), trim(ListBox2.Items.Text));

If you can do this, I will open a New Question for you as well. just like before.
Thanks Russell.
Thats ok Russell.
I am doing this way instead. Still using the RichEdit(s)

RichEdit2.Text := '"',
RichEdit3.Text := '''',
RichEdit1.Text:=FastReplace(RichEdit1.Text, trim(RichEdit2.Text), trim(RichEdit3.Text));
RichEdit2.Clear;
RichEdit3.Clear;
RichEdit2.Text := '|',
RichEdit3.Text := '","',
RichEdit1.Text:=FastReplace(RichEdit1.Text, trim(RichEdit2.Text), trim(RichEdit3.Text));

And so on...

This may not be the best way to do it, but it works pretty good.
First it grabs the Text in RichEdit2, then Replaces the RichEdit1 with the Text in RichEdit3.
Then after this, It loops back and Clears the 2-RichEdits.
Then it does the same routine over again, until it reaches the end.
----------
If you can think of a better way to achive this, I am all ears (Or Eyes as the case may be on here).

Take Care Russell, just wanted to let you know that it is working thus far.
And it is pretty fast.
Over 7,000+ Character Changes in less then 5-Seconds.

Wayne

Wayne, question for you....

Are you *always* checking for a single char value in the search / replace (I see that the replacement can be is sometimes a string, eg: '","'). If so, then I can help you optimize this slightly.

If you are searching for strings (char or string), then the only performance gain would be in peforming ALL the translations before assigning the string back to the RichEdit control. This is the biggest time killer for you, as each time you get/set the .Text property of the richedit control, you force the system to allocate memory. You require at least one get /set in order to process and assign back, but as you have it this occurs for each search and replace that occurs. If you know all the seach/replace values ahead of time, the code could be structured as follows:

get RichEdit1.Text
{
  while search/replace { search/replace}
}
set RichEdit1.Text

---

Regards,
Russell


If you are performing single char to char/string translations, then the following should prove to be extremely fast.

  // Function takes: input string, chars to translate, chars/strings to translate to
  RichEdit1.Text:=FastTranslate(RichEdit1.Text, ['"', '|'], ['''', '","']);


function FastTranslate(const Text: String; const FromChars: Array of Char; const ToStrings: Array of String): String;
var  lpszaTrans:    Array [#0..#255] of String;
     lpbaTrans:     Array [#0..#255] of Boolean;
     lpszResult:    PChar;
     dwLength:      Integer;
     dwString:      Integer;
     dwIndex:       Integer;
begin

  // Get source text length
  dwLength:=Length(Text);

  // Nothing to process if text is empty
  if (dwLength = 0) then
     // Return empty string
     SetLength(result, 0)
  else
  begin
     // The char array and string array length must match
     if (High(FromChars) <> High(ToStrings)) then
        // Trow exception
        raise EConvertError.Create('Tranlation arrays must have the same item count!')
     else if (High(FromChars) < 0) then
        // No items in array
        result:=Text
     else
     begin
        // Set default translation string length
        dwString:=0;
        // Get largest string length
        for dwIndex:=0 to High(ToStrings) do
        begin
           if (Length(ToStrings[dwIndex]) > dwString) then dwString:=Length(ToStrings[dwIndex]);
        end;
        // Clear the translation tables
        FillChar(lpszaTrans, SizeOf(lpszaTrans), 0);
        FillChar(lpbaTrans, SizeOf(lpbaTrans), 0);
        // Build the translation table
        for dwIndex:=0 to High(FromChars) do
        begin
           // Translation for char is set
           lpbaTrans[FromChars[dwIndex]]:=True;
           // Set the translation string
           lpszaTrans[FromChars[dwIndex]]:=ToStrings[dwIndex];
        end;
        // Allocate maximum memory required for result
        SetLength(result, Succ(dwLength * dwString));
        // Cast result as pchar
        lpszResult:=Pointer(result);
        // Walk the source string and perform the translation
        for dwIndex:=1 to dwLength do
        begin
           // Check for translation
           if lpbaTrans[Text[dwIndex]] then
           begin
              // Get translation string length
              dwString:=Length(lpszaTrans[Text[dwIndex]]);
              // Check for zero byte copy
              if (dwString > 0) then
              begin
                 // Copy translation string
                 StrPCopy(lpszResult, lpszaTrans[Text[dwIndex]]);
                 // Push result by number of bytes copied
                 Inc(lpszResult, dwString);
              end;
           end
           else
           begin
              // Copy byte
              lpszResult^:=Text[dwIndex];
              // Push result pointer
              Inc(lpszResult);
           end;
        end;
        // Reset string to actual number of bytes copied
        SetLength(result, lpszResult-PChar(result));
     end;
  end;

end;
Thank you Russell.
I will take a more closer look at this in a while.
One question if you do not mind?

RichEdit1.Text:=FastTranslate(RichEdit1.Text, ['"', '|'], ['''', '","']);

Between each of the Brackets [ ]
The first one shows:
['"', '|']        // What is the    |    called? I cannot find it's name, which is really frustrating?
['''', '","']

I do not think that I am following this quite right?
On the First one:          "   and  |
On the 2nd ons :          '    and  ","

Does that mean that everything that is in the 1st [ ]  will be replaced with the information that is
Located in the 2nd [ ] of the following order?
I think that is what this means?

Right now with the way that I have it, it is showing: (Just something to let me know "where" & "what" it is doing.)
=========================================
Label1.Caption := 'Replacing the Comma''s';  // Added in for Visualization of Content Editing
Label1.Refresh;  // Need to Refresh the Label in order to grab the above text, if not, it will not show.
RichEdit2.Text := '"',
RichEdit3.Text := '''',
RichEdit1.Text:=FastReplace(RichEdit1.Text, trim(RichEdit2.Text), trim(RichEdit3.Text));
Label1.Caption := 'Replacing the |';  // Added in for Visualization of Content Editing
Label1.Refresh; // Need to Refresh the Label in order to grab the above text, if not, it will not show.
RichEdit2.Clear;
RichEdit3.Clear;
RichEdit2.Text := '|',
RichEdit3.Text := '","',
RichEdit1.Text:=FastReplace(RichEdit1.Text, trim(RichEdit2.Text), trim(RichEdit3.Text));
//This next line show only at the end, after everything is done.
Label1.Caption := 'Completed Successfully'; // This of course lets me know that it is completed, As the last 2-Processes
// Do not show a [Hour Glass Cursor] do to the time elapse that is accuring in the drawn-out adding process of lines.
=========================================

With all that it is doing in the above lines, which in the project that is happening 12-times.
Along with a few other actions that are also taking place (All under the same [Button])
It takes an average of about 25-seconds to run this action against a .txt file containg 5,135 lines.
With a Character count starting at:7,395,940          And ending with:8,463,547 <-- Ending with is after everything is
done and over. With a Total adding Character Count of: 1,067,607
(Do not ask why I included all this information in here about the Character Count, I just thought I needed too?)

I have to check out a few things on the "Web Site" end of this Project, once I have dealt with it, I will check out
The code that you have supplied here.

Thanks again Russell;
Wayne

>> Does that mean that everything that is in the 1st [ ]  will be replaced with the information that is Located in the 2nd [ ] of the following order? I think that is what this means?

Yes, that is exactly what it means. If the items being searched on are single chars (as in your example), then a bulk match / replace can be performed, eg:

" becomes '
| becomes "," // its a pipe

etc, etc. You should test it out if you get a chance; you should find that it reduces your processing time.

Regards,
Russell
Hello Russell.

Well, I have to Add in a LOT of text. And I am not sure if the [FastTranslate] will work or not?

This is what I am up against.
I have to look in the file for different sequences of Number. Once I have found the Numbers, I need to add
In some additional number right after the Number-Set is found.
Example:

","12180101","   <-- this is what I am hunting for.
,'","12180101","'  <-- this is how I wrote it.

","12180101","91","  <-- This is how I have to replace it, and still keep the number-set in tact.
,'","12180101","91","',  <-- This is how I wrote it.

All together it will look something like this:

RichEdit1.Text:=FastTranslate(RichEdit1.Text, ['"', '|'], ['''', '","']);

RichEdit1.Text:=FastTranslate(RichEdit1.Text, ['","10090400","','","10090700","','","10110000","'],['","10090400","91","','","10090700","92","','","10110000","93","']);


In the above example, I run up on this Error:
Incompatible Types:  'Char' and 'String'
And it is landing on "Every" location where this is:       ','     <-- Which is the Devider between the 2 Strings.


I am not sure if this could will do this, but if it does, that would be great, if not, then I will need to do a lot
Of what I have listed above that I ended up doing, which it took long enough to make up this code here.
It will take double the time to do the other way of coding.

Thanks Russell for any "hopefully" good news/information that you can assist me with.
Hopefully real soon :)

Wayne
You need to re-read what I originally posted:

>> If you are performing single char to char/string translations, then the following should prove to be extremely fast.

You are doing string -> string translations, thus, the code is complaining because

RichEdit1.Text:=FastTranslate(RichEdit1.Text, ['","10090400","','","10090700","','","10110000","'],['","10090400","91","','","10090700","92","','","10110000","93","']);

contains strings in the array (first array) that only accepts chars.

This, on the otherhand, is ok:

RichEdit1.Text:=FastTranslate(RichEdit1.Text, ['"', '|'], ['''', '","']);

As I originally stated for multiple string replacements:
>> If you are searching for strings (char or string), then the only performance gain would be in peforming ALL the translations before assigning the string back to the RichEdit control. This is the biggest time killer for you, as each time you get/set the .Text property of the richedit control, you force the system to allocate memory. You require at least one get /set in order to process and assign back, but as you have it this occurs for each search and replace that occurs. If you know all the seach/replace values ahead of time, the code could be structured as follows:

get RichEdit1.Text
{
  while search/replace { search/replace}
}
set RichEdit1.Text


Regards,
Russell
I figured that was it.
I went back and read it and new that it was for Chars.
After I went in and did the list.
But no big deal.

Thanks for you "reminded" info.

Take Care Russell.
Wayne
No problem Wayne...

As I said though, if you get the text once, make all your replacements to it, then set it back to the richedit control, you can probably save yourself some processing time.

Russell