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

asked on

Extract Lines from 1st RichEdit - to - 2nd RichEdit

Hello All;

  Let me try to explain this a little.

I am working with "Datafeed" Files. From different companies over their product inventory.
I am using a program that I am developing to do all the work with the files.
I have to edit these "Datafeeds" before [Importing] them into an Access Database.

This is the problem that I have come across.

The lines are listed as such

"1","345456","some product","some information here","45.65","more information","Bad information"
"1","788589","some product","some information here","56.65","more information","Good information"

From the example of the line(s) listed above.
I need to do the following.

I need to have some sort of process that will iterate through the .txt Datafeed file
And extract out all the [Lines] that have a certain word-set in it. In this example it would be "Bad information"
I then need for the Line(s) to be inserted into another TRichEdit control for further editing.

Run-Down
Extract Certain User Chosen line(s) from 1-RichEdit - to another - RichEdit.

Thanks all
I know this one is probably going to be a pain, so I am going to start with the Highest [500-Points]

Thanks All;
Carrzkiss
Avatar of Wayne Barron
Wayne Barron
Flag of United States of America image

ASKER

Updated Information:

When the Line(s) are Extracted out of the 1st TRichEdit.
I need them to also be [Deleted] from the 1st TRichEdit.
And only show in the 2nd TRichEdit.

So. The 1st TRichEdit will not have no "Bad information" line(s) in it.

Thanks All;
Carrzkiss

Here is an extension to the string list class that will allow you to perform simple "filtering" on column values on CSV data. (like you have above). You should find it pretty simple to extend the filtering if needed. I am posting the example usage first, then the source. Let me know if there are questions/problems.

Regards,
Russell

--------- example usage -------------

procedure TForm1.Button1Click(Sender: TObject);
var  csvList:       TCSVList;
begin

  RichEdit1.Lines.LoadFromFile('c:\test.txt');

  // Example code
  csvList:=TCSVList.Create;

  // Resource protection
  try
     // Load from rich edit
     csvList.Text:=RichEdit1.Text;
     // Filter the text, returns number of filtered rows
     if (csvList.Filter(6, foNEQ, 'Bad information') > 0) then
     begin
        // Update both richedit contols
        RichEdit1.Text:=csvList.FilterList.Text;
        RichEdit2.Text:=RichEdit1.Text;
     end;
  finally
     // Free the list
     csvList.Free;
  end;

end;

--------- unit source -------------

unit CSVData;
////////////////////////////////////////////////////////////////////////////////
//
//   Unit        :  CSVData
//   Author      :  rllibby
//   Date        :  05.24.2006
//   Description :  Extended version of TStringList that allows for filtering and
//                  handling of comma sep data.
//
////////////////////////////////////////////////////////////////////////////////
interface

////////////////////////////////////////////////////////////////////////////////
//   Include units
////////////////////////////////////////////////////////////////////////////////
uses
  Windows, SysUtils, Classes;

////////////////////////////////////////////////////////////////////////////////
//   Type declarations
////////////////////////////////////////////////////////////////////////////////
type
  TFilterOperator   =  (foEQ,      foNEQ,      foGT,       foLT,
                        foGTEQ,    foLTEQ,     foContains, foNotContains,
                        foNULL,    foNotNULL);

////////////////////////////////////////////////////////////////////////////////
//   Class declarations
////////////////////////////////////////////////////////////////////////////////
type
  TCSVList          =  class(TStringList)
  private
     // Private declarations
     FParseList:    TStringList;
     FFilterList:   TStringList;
  protected
     // Protected declarations
     function       GetField(Row, Col: Integer): String;
  public
     // Public declarations
     constructor    Create;
     destructor     Destroy; override;
     function       Filter(ColumnIndex: Integer; Operator: TFilterOperator; Criteria: String; IgnoreCase: Boolean = False): Integer;
     property       FilterList: TStringList read FFilterList;
  end;

implementation

function TCSVList.Filter(ColumnIndex: Integer; Operator: TFilterOperator; Criteria: String; IgnoreCase: Boolean = False): Integer;
var  dwIndex:       Integer;
     bAllow:        Boolean;
     szCriteria:    String;
     szValue:       String;
begin

  // Lock filter list update
  FFilterList.BeginUpdate;

  // Resource protection
  try
     // Clear the filter list
     FFilterList.Clear;
     // Convert the criteria
     if IgnoreCase then
        szCriteria:=Lowercase(Criteria)
     else
        szCriteria:=Criteria;
     // Walk our string list
     for dwIndex:=0 to Pred(Count) do
     begin
        // Parse the column value
        if IgnoreCase then
           // Get lower case version of string
           szValue:=LowerCase(GetField(dwIndex, ColumnIndex))
        else
           // Get string
           szValue:=GetField(dwIndex, ColumnIndex);
        // Perform the operator comparison
        case Operator of
           foEQ           :  bAllow:=(szValue = szCriteria);
           foNEQ          :  bAllow:=(szValue <> szCriteria);
           foGT           :  bAllow:=(CompareStr(szValue, szCriteria) > 0);
           foLT           :  bAllow:=(CompareStr(szValue, szCriteria) < 0);
           foGTEQ         :  bAllow:=(CompareStr(szValue, szCriteria) >= 0);
           foLTEQ         :  bAllow:=(CompareStr(szValue, szCriteria) <= 0);
           foContains     :  bAllow:=(Pos(szCriteria, szValue) > 0);
           foNotContains  :  bAllow:=(Pos(szCriteria, szValue) = 0);
           foNULL         :  bAllow:=(Length(szValue) = 0);
           foNotNULL      :  bAllow:=(Length(szValue) > 0);
        else
           // Should never execute
           bAllow:=(szValue = szCriteria);
        end;
        // Add the string to the filter list if allowed
        if bAllow then FFilterList.Add(Strings[dwIndex]);
     end;
  finally
     // Unlock the list
     FFilterList.EndUpdate;
  end;

  // Return count of filtered rows
  result:=FFilterList.Count;

end;

function TCSVList.GetField(Row, Col: Integer): String;
begin

  // Get column value from data row
  FParseList.CommaText:=Strings[Row];

  // Check column index
  if (Col < 0) or (Col >= FParseList.Count) then
     // Return empty string
     result:=EmptyStr
  else
     // Return the column value
     result:=FParseList[Col];

end;

constructor TCSVList.Create;
begin

  // Perform inherited
  inherited Create;

  // Create internal lists
  FParseList:=TStringList.Create;
  FFilterList:=TStringList.Create;

end;

destructor TCSVList.Destroy;
begin

  // Resource protection
  try
     // Free the parse list
     FParseList.Free;
     // Free the filter list
     FFilterList.Free;
  finally
     // Perform inherited
     inherited Destroy;
  end;

end;

end.


Hello Russell.

 Is there something that I need to add to the   Uses;  ?
I added the component to the Lib\ Directory in an already searchable path.
What else do I need to perform?

Thanks a bunch.
Its a class (not  a component), so it just needs to be added in the uses clause of your project. Sample follows

Russell

----------

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ComCtrls, CSVData;

type
  TForm1 = class(TForm)
    Button1: TButton;
    RichEdit1: TRichEdit;
    RichEdit2: TRichEdit;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation
{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var  csvList:       TCSVList;
begin

  RichEdit1.Lines.LoadFromFile('c:\test.txt');

  // Example code
  csvList:=TCSVList.Create;

  // Resource protection
  try
     // Load from rich edit
     csvList.Text:=RichEdit1.Text;
     // Filter the text, returns number of filtered rows
     if (csvList.Filter(6, foNEQ, 'Bad information') > 0) then
     begin
        // Update both richedit contols
        RichEdit1.Text:=csvList.FilterList.Text;
        RichEdit2.Text:=RichEdit1.Text;
     end;
  finally
     // Free the list
     csvList.Free;
  end;

end;

end.

-- dfm --
object Form1: TForm1
  Left = 284
  Top = 114
  Width = 687
  Height = 368
  Caption = 'Form1'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 8
    Top = 8
    Width = 75
    Height = 25
    Caption = 'Button1'
    TabOrder = 0
    OnClick = Button1Click
  end
  object RichEdit1: TRichEdit
    Left = 8
    Top = 40
    Width = 329
    Height = 281
    Lines.Strings = (
      'RichEdit1')
    ScrollBars = ssBoth
    TabOrder = 1
    WordWrap = False
  end
  object RichEdit2: TRichEdit
    Left = 340
    Top = 40
    Width = 329
    Height = 281
    Lines.Strings = (
      'RichEdit1')
    ScrollBars = ssBoth
    TabOrder = 2
    WordWrap = False
  end
end
It kinda helps if you spell it correctly :(

I had it as:    CVSData

It would have compiled when I first tried it, but...
Sorry about that, did not mean to sound dumb on that one.

And yes, I knew it was a "class" but wrote "component" for some reason.
Man what a day this has been.

Going to test it out now, will reply back in a few.

Wayne
This code "copies" out "everything" in the Richedit. But does not [Delete] it.
I need it to "only" Copy out the lines that include the   if (csvList.Filter(6, foNEQ, 'Bad information') > 0) then
And then [Delete] out Only the Lines from the "RichEdit1" that have been added to the RichEdit2.

Sorry, I am not following you. The code example I gave does the following:

The richedit 1 starts with everything, the filtering removes the items with "Bad information". The example I gave is looking for "Bad Information" (exact text, so if its something else, you need to update the code accordingly). It then updates richedit 1 with the filtered lines (those that do not contain "Bad information", and copies the newly updated text to richedit 2.

So when you say delete, what are you talking about? Removing the lines with "bad information"? If so, do you want those lines discarded, moved to the second richedit, or what? Perhaps you could explain in logical steps the functionality you are looking for (its just a slight communication error Im sure).

Regards,
Russell
I have tried everything, and I cannot get your Project to work.

I loaded the [RX Library 2.75] Readme.txt file.
And inserted the following text into code:   'RX Library 2.75' to have removed.
And it still showed the text in both of the TRichEdit's.


----------------
Going to give this another shot. Hopefully it will be more clearer, though I believe I was very clear once before.
But, sometimes it takes a little more explaining, as we all know.
----------------

The file is loaded into the RichEdit1.
Need to scan the file for a paticular "Phrase" (or) "Word(s)" (Using a TEdit.Text for inserting what needs to be searched)
Once the file is scanned, the [Line (or) Lines] that have the Phrase (or) Word(s)
Needs to be copied to the other RichEdit2.

If possible, I need to have the [Line (or) Lines] moved from the RichEdit1 to the RichEdit2.
Reason: I need to work with these line(s) seperately from the rest of the Datafeed in the .txt file.

Once I have the Lines that are in RichEdit1 inserted into the Access Database, (Excluding the once that
Are transferred to RichEdit2) then I will begin working on inserting the line(s) from RichEdit2, Editing them to go inside the Access Database as well.

So as you can see, it is a must that the lines are moved to the other RichEdit2.
Not the Entire Files (Like your Demo does)
But 'Only' the ones that were Searched for.
And then the Line(s) are [Deleted] from RichEdit1

I really do hope that this is clear enough to understand.

Wayne
Yes, I follow you just fine.... but what you posted above is a little (actually VERY) different than your original question.... Is the text to be handled in comma seperated format or not? The text file you mention, and I'll quote part of it:

<quote>

RX Library 2.75
===============

The Set of Native Delphi Components for Borland Delphi
versions 1, 2, 3, 4 & 5 and Borland C++ Builder 1, 3 & 4.
100% Source Code.

Last revision date Oct 12, 1999.

PLEASE FOLLOW THE INSTRUCTIONS PROVIDED IN THE INSTALLATION SECTION!


TABLE OF CONTENTS
-----------------
Latest Changes
Overview
History
License Agreement
Installation
Demonstration Programs
Source Files
Using GIF Images
Copyright Notes

<end quote>

----

Is not in CSV format like your original question stated. If you follow the code I gave you, it is for filtering a row of CSV data by a specific column. This followed what you originally gave:

"1","345456","some product","some information here","45.65","more information","Bad information"
"1","788589","some product","some information here","56.65","more information","Good information"

as column 6 (counting from 0) is the column that needed to be filtered on. The operators provided were:

foEQ = Equals
foNEQ = Not equals
foGT = Greater than
foLT = Less than
foGTE = Greater than or equals
foLTEQ = Less than or equals
foContains = Contains
foNotContains = Does not contain
foNULL = Equals empty string
foNotNULL = Does not equal empty string

And if you were parsing csv data, would allow you to filter the data on a given column, add the rows (strings) to the second richedit, then refilter on the opposite (reverse the filter) and add those rows to the first richedit.

If you are only searching for a paticular string of text, and do not care where it falls within the data, then please let me know, as a simple Pos function can be used on the data. (eg, the string either contains or does not contain the substring). The misunderstanding was on the data to be parsed, which from what you first stated was in CSV format.

Russell
OK, I scr**ed up on the RX Lib file.
Do not know what I was thinking on that one.

OK.
Just tested it out with your Extra provided information here.
On Column 6 And grabbed just that "Main_Cat" <-- This is what is there in the real file.
OK.
After it grabbed just "Main_Cat" from RichEdit1 and inserted just "Main_Cat" into RichEdit2.
(That is not right, I need the Entire "Line")

After it copied "Main_Cat" to RichEdit2.
(This is Bad)
It Deleted 1/2 of ALL Lines from RichEdit1. This is very bad.

So, yes.
To answer the bottom question you asked:
The information is going to be "anywhere" so it cannot contain a hardcoded #
And it needs to get "Every Line" Not just the single "word"
It could be as many as 1 - 3000+ lines that needs to be extracted from RichEdit1 -to- RichEdit2.

------------
Man I am so sorry for the scr** up I did above.
I just do not know what the heck I was thinking.

ASKER CERTIFIED SOLUTION
Avatar of Russell Libby
Russell Libby
Flag of United States of America image

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
Works Great !!!!!!!!!!!!!!!!!!!!!!!!

I had to do some playing around with my code to make it work in my project.
I have the RichEdit(s) on TabSheets.
So in order to make the Data show in the RichEdit2, I have to do
===================================
 ProcessData(reFrom, reTo, Edit1.Text);
     finally
        Screen.Cursor:=crDefault;
 TabSheet5.Show;
 RichEdit2.Visible := True;
 RichEdit2.Refresh;
     end;
===================================
Once I did that, and ran my Project. SUPPER!!!!!!
Worked like a charm.

-------------
Once again, I am sorry for the mess up I did earlier, in mixing up everything.
I was not thinking straight at all, which has been my entire day today.
If I could give out more points past the 500-Points Mark, I would Double it for you,
Just to take care of the aggrivation that I put you through today and this evening.

I will try to make it up to you one day with something else that you assist me with.

Thank you so much Russell.
You have helped me out a lot on this project, with this issue and others.
You have proven yourself over and over again as a real Experts in EE, where some are false.

Thank you once again and take care.

Wayne

I was glad to help Wayne; and I was not frustrated, due to having worked with you in the past. I knew it was just a slight communication error and that we would be able to work it out.. no harm, no foul. Glad all is working as expected ;-)

Russell



Thanks Russell.

Take Care and have a good evening.

Wayne
Hello Russell;
(If you can do something like this, I will open a new question and post the link in here)

Similuar to this line (I would think)
if (csvList.Filter(6, foNEQ, 'Bad information') > 0) then

I need to look for in the "same file" a certain area and see if it contains a
http://
And if it does not. Then I need to enter:
"something"

Example:

"1","345456","some product","some information here","45.65","more information","http://someurl"
"1","788589","some product","some information here","56.65","more information","Category One",'Good information","http://someurl"

In the above Example:
1st line Does Not contain the word:   "Category One"
2nd line Does contain the word:        "Category One"

What I need to do is look at each line and see if at a "Certain Point" the word is found, and if it is not
Then I will need to [Add] in the word that is needed.
In the case of the Example above.
I would search the 2-lines for the word "Category One" at the [6] location and if I find "http://
Then I will need to insert "Before" "http://" with the word "Category One"

I hope that is a good explanation?
Wayne
First Line - Con
Never mind.
A simple [Search & Replace] will work on this one.
Do not know why I did not think of it to begin with.

Take Care Russell
I was incorrect.
After testing out my theory, I found that there are "2" identical [Locations] in the file that contact
The start of the Company http:// link.
So I will be unable to just simply do a [Search & Replace] function.

So, if you think that you might be able to do the original question, that would be great.

---------Original Message--------------
Hello Russell;
(If you can do something like this, I will open a new question and post the link in here)

Similuar to this line (I would think)
if (csvList.Filter(6, foNEQ, 'Bad information') > 0) then

I need to look for in the "same file" a certain area and see if it contains a
http://
And if it does not. Then I need to enter:
"something"

Example:

"1","345456","some product","some information here","45.65","more information","http://someurl"
"1","788589","some product","some information here","56.65","more information","Category One",'Good information","http://someurl"

In the above Example:
1st line Does Not contain the word:   "Category One"
2nd line Does contain the word:        "Category One"

What I need to do is look at each line and see if at a "Certain Point" the word is found, and if it is not
Then I will need to [Add] in the word that is needed.
In the case of the Example above.
I would search the 2-lines for the word "Category One" at the [6] location and if I find "http://
Then I will need to insert "Before" "http://" with the word "Category One"

I hope that is a good explanation?
Wayne
Forget about me today.

Though by URL's were Idential in the majority of the characters used.
"1" thing stood out: The file itself .jpg

Example
http://company1.com/folder/sub-folder/T_2334_1.jpg

I needed to add information to the start of the URL to the image to make the Database Table read
Correctly while during the Import.

So.
I could not do a search for: http://company1.com/folder/sub-folder/
Because there is "2" for each line of the above URL

1st line (Like the above)
http://company1.com/folder/sub-folder/T_2334_1.jpg

2nd lin
http://company1.com/folder/sub-folder/F_2334_1.gif

So what I had to do in order to only add in the information I needed was to look at the     /F_
So Search for:
","http://company1.com/folder/sub-folder/F
Replace with
","No Info","http://company1.com/folder/sub-folder/F

Got it now.
So, everything "should" run smoothly now.

Wayne
I give up on this one.
After closer eximination, there is a set of Numbers in the URL path that "changes" on every item.

Example
74910/F_74910_1.jpg
As you can see the    74910
Is the Folder name and the [Image] # as well.

So, let me know if you want to takle this one or not?

(I tried everything, I should have tried everything before posting to the topic, and I would not have
Added in so much non-sence information that I tested that did not work)

Take Care Russell

Wayne
lol...

Wayne,
Have you taken a look at regular expression parsing? This may be what you need to handle the conditional parsing. If you can post some "actual" examples of the data that you are attempting to filter on (or even search/replace), then I may be able to help further.

Regards,
Russell
OK
Here is a file with "2" actual lines.

http://www.carrz-fox-fire.com/b/Document.txt

On the 2nd line you will find    ","Web Only","
The 1st line does not have it. which is messing up the[Import] into Access

I need to be able to search through the entire document and look for:  (A guess section would be)
","http:/
As a starting point. which it on section [29]
So if it finds ","http:/  the it will know that it needs to add information before ","http:/
Which would be something like this   ","Something here","http:/
(This is what I tried in the Search/Replace but as you will be able to see in the attached file
The Image name and ImageFolder name are the same, so the Search/Replace cannot work in theory)

Let me know if you need any other information
p.s.
The actual URL's have been edited

Wayne

Perhaps another way to look at this...
Have you though about the fact that the rows do not have the same number of columns in them? If you check the 30th column and find that it starts with "http://", then you would know that a column needed to be inserted there. It really depends on how consistent the column structure is within your data file.

Russell

Right.
Yes,

Dept_Name","         <-- Column #29        ","Web Only","
Large_Product_Image","   <-- Column #30        ","Image URL","

Some of the lines do not have the [Column #29 Dept. Name] And we need a way to add in an additional Column
Even though the Column #29 for the ones that are added in will not consist of an actual Store Dept. Name
I will feed it as something like:    ","[No Dept Available]","     in place of the ","Web Only","

This will in return moves the columns over to where they need to be at to create the Table (or)
Add into an Existing Table

Hope that helps.
I will put something together later this evening or in the morning, as I am off to the movies ;-)

Russell
Which Movie?
Have fun!!

Wayne
Wayne,
DaVinci code, and it was a nice break from the week.

One question in regards to the data processing. Do you need a function that takes a file/stringlist/richedit and returns the updated data. Or do you need something like the original question where correct lines are left in the first richedit, and updated lines are placed in the second. I am guessing its the first, but let me know if that is incorrect.

Russell
The 1st would be better. And more preferred.

Davinci Code.
Have not had an opertunity to see it yet, hopefully sometime soon.
Glad you enjoyed it.

Wayne
Give the following a try (example usage first, procedure follows)

   // Assuming richedit2 contains the data to be update
   UpdateDeptData(RichEdit2.Lines, '[No Department Available]');


// Procedure to update the TStrings(TStringList) data so that the department field is inserted
// if missing from the row
procedure UpdateDeptData(Data: TStrings; DeptData: String);
var  listData:      TStringList;
     listFields:    TStringList;
     dwIndex:       Integer;
     dwFields:      Integer;
     szRow:         String;
begin

  // Lock the string list
  Data.BeginUpdate;
  try
     // Create string list for field processing
     listFields:=TStringList.Create;
     try
        // Walk the list items
        for dwIndex:=0 to Pred(Data.Count) do
        begin
           // Clear field processing string
           szRow:=EmptyStr;
           // Parse list into individual fields
           listFields.CommaText:=Data[dwIndex];
           // Check field count and value of the 29th field
           if (listFields.Count = 31) and (Pos('http://', LowerCase(listFields[29])) = 1) then
           begin
              // Insert department field
              listFields.Insert(29, DeptData);
              // Recreate the quoted comma sep string
              for dwFields:=0 to Pred(listFields.Count) do szRow:=szRow+AnsiQuotedStr(listFields[dwFields], '"')+',';
              // Remove trailing comma
              Delete(szRow, Length(szRow), 1);
              // Update the data
              Data[dwIndex]:=szRow;
           end;
        end;
     finally
        // Free fields list
        listFields.Free;
     end;
  finally
     // Unlock the string list
     Data.EndUpdate;
  end;

end;
You are the best Russell;
http:Q_21865362.html

You deserve more, but this is all that it will allow me to give you.
That code worked like an absolute charm :)

Wayne
Thanks much (you didn't have to though), I will post the code in the linked question.

Have a great weekend,
Russell
You took the time to assist me once again, so you deserve it.

Have a good weekend yourself.

Wayne