?
Solved

Regex issue (maybe...)

Posted on 2014-04-09
11
Medium Priority
?
320 Views
Last Modified: 2014-04-15
In my application I load a file (which is the result of an export from the Windows registry) in a TListBox (see this question for further details: http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_28408002.html#a39988447). I use regular expression to detect paths within the file and I check if the path exists in the current machine: if not, the listbox item is highlighted.

While adding items to the listbox, I build a second stringlist which holds the number of item withnon existent paths, so I can use this info with two buttons (Next and Prev) to browse the listbox jumping from a wrong path to another.

The problem is that the code to highlight draws colored background on certain items which have not been inserted in the listbox which holds indexes.

Maybe seeing the code can help you to understand me better:-)


Fill Listbox:
procedure TfrmIncorrectPaths.FormShow(Sender: TObject);
var
  rx: TPerlRegEx;
  I: Integer;
  sr: TStreamReader;
  n: Integer;
  Buf: array[1..8191] of Char;  { 4K buffer }
begin
  lblCount.Caption := '';
  try
    slOriginal.LoadFromFile(FName);
    if (bitActual = '64') and (bitBackup = '32') then
      AdjustProgramDir32to64(slOriginal);
    if (bitActual = '32') and (bitBackup = '64') then
      AdjustProgramDir64to32(slOriginal);
    rx := TPerlRegEx.Create;
    try
      rx.Options := [preCaseLess,preExtended];
      rx.RegEx := '\b[a-z]:\\                    # Drive'#10 +
                  '(?:[^\\/:*?"<>|\r\n]+\\)*     # Folder'#10 +
                  '[^\\/:*?"<>|\r\n]*            # File';
      for I := 0 to slOriginal.Count - 1 do
      begin
        rx.Subject := slOriginal[I];
        if rx.Match then
        begin
          lbxRegFile.Items.Add(slOriginal[I]);
          if (not DirectoryExists(ExtractFilePath(rx.MatchedText))) then
          begin
            if slTokens.IndexOfName(IntToStr(I)) = - 1 then
              slTokens.Add(IntToStr(I)+'='+'1')
            else
            begin
              n := StrToInt(slTokens.ValueFromIndex[slTokens.IndexOfName(IntToStr(I))]);
              n := n + 1;
              slTokens.ValueFromIndex[slTokens.IndexOfName(IntToStr(I))] := IntToStr(n);
            end;
          end;
        end;
      end;
    finally
      rx.Free;
    end;
  finally
  end;
end;

Open in new window


Draw background:
procedure TfrmIncorrectPaths.lbxRegFileDrawItem(Control: TWinControl;
  Index: Integer; Rect: TRect; State: TOwnerDrawState);
var
  myColor: TColor;
  myBrush: TBrush;
  rx: TPerlRegEx;
  I: Integer;
begin
  myBrush := TBrush.Create;
  try
    rx := TPerlRegEx.Create;
    try
      rx.Options := [preCaseLess,preExtended];
      rx.RegEx := '\b[a-z]:\\                    # Drive'#10 +
                  '(?:[^\\/:*?"<>|\r\n]+\\)*     # Folder'#10 +
                  '[^\\/:*?"<>|\r\n]*            # File';
      rx.Subject := lbxRegFile.Items[Index];
      myColor := clWhite;
      if rx.Match then
        if (not DirectoryExists(ExtractFilePath(rx.MatchedText))) then
          myColor := clAqua;
      with (Control as TListBox).Canvas do
      begin
        myBrush.Style := bsSolid;
        myBrush.Color := myColor;
        Winapi.Windows.FillRect(handle, Rect, myBrush.Handle) ;
        Brush.Style := bsClear;
        TextOut(Rect.Left, Rect.Top,
                 (Control as TListBox).Items[Index]) ;
      end;
    finally
      rx.Free;
    end;
  finally
    MyBrush.Free;
  end;
end;

Open in new window


And finally the code to navigate the listbox:
procedure TfrmIncorrectPaths.imgNextTokenClick(Sender: TObject);
var
  CurrentIndex: Integer;
begin
  if (lbxRegFile.ItemIndex < StrToInt(slTokens.Names[0]))
      or (lbxRegFile.ItemIndex > StrToInt(slTokens.Names[slTokens.Count-1])) then
  begin
//    ShowMessage(slTokens.Names[0]);
    CurrentIndex := 0;
    lbxRegFile.ItemIndex  := StrToInt(slTokens.Names[0])
  end
  else
  begin
    CurrentIndex := slTokens.IndexOfName(IntToStr(lbxRegFile.ItemIndex));
//    ShowMessage(slTokens.Names[CurrentIndex+1]);
    lbxRegFile.ItemIndex := StrToInt(slTokens.Names[CurrentIndex+1]);
  end;
  lblCount.Caption := 'Line '+IntToStr(lbxRegFile.ItemIndex)+' - Wrong values: '+slTokens.ValueFromIndex[CurrentIndex+1];
end;

procedure TfrmIncorrectPaths.imgPrevTokenClick(Sender: TObject);
var
  CurrentIndex: Integer;
begin
  if (lbxRegFile.ItemIndex < StrToInt(slTokens.Names[0]))
      or (lbxRegFile.ItemIndex > StrToInt(slTokens.Names[slTokens.Count-1])) then
  begin
    CurrentIndex := 0;
    lbxRegFile.ItemIndex  := StrToInt(slTokens.Names[slTokens.Count-1]);
  end
  else
  begin
    CurrentIndex := slTokens.indexOfName(IntToStr(lbxRegFile.ItemIndex));
    lbxRegFile.ItemIndex := StrToInt(slTokens.Names[CurrentIndex-1]);
  end;
  lblCount.Caption := 'Line '+IntToStr(lbxRegFile.ItemIndex)+' wrong values: '+slTokens.ValueFromIndex[CurrentIndex-1];
end;

Open in new window


I attach a sample file I'm testing with (its name is passed by the calling form which opens the form where the listbox is in).

I also attach two screenshots of the just created form and the form after the click on the Next button: as you can see, it doesn't jump the the first highlighted line of the first screenshot but to line 108.

Just createdAfter the first jump
Thanks to all
Marco
HKCU-basic.txt
0
Comment
Question by:Marco Gasi
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 5
11 Comments
 
LVL 46

Expert Comment

by:aikimark
ID: 39988683
Rather than use a separate data structure, why not use the ItemData property as the basis of your button navigation?  As you build your listbox, set the ItemData property at the same place as your highlighting.  The ItemData values for these exceptional items will be an ascending sequence of integers.

That way, your button code can use the getitemdata method to point to the index of the first/next/prev items in the listbox.
0
 
LVL 46

Expert Comment

by:aikimark
ID: 39988737
Are you sure your regex pattern is giving you the correct path to check?
0
 
LVL 31

Author Comment

by:Marco Gasi
ID: 39988789
No, I see some non existent path is not highlighted: for instance in line 13 there is non existent path C:\Users\marqus\AppData\Local\Microsoft but it's not highlighted whereas following paths are correctly highlighted.

Anyway, I'm going to look for ItemData property.
0
VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

 
LVL 31

Author Comment

by:Marco Gasi
ID: 39989016
About ItemData, I don't think that's useful for me: my navigation buttons don't have to jump from aline to the following line. They need to know the index of the following line with a wrong path, so in my econd data structure I store only the indexes of the items with a wrong path. Perhaps I'm wrong, but it deosn't seem ItemData can be ueful in this...
0
 
LVL 46

Expert Comment

by:aikimark
ID: 39989066
Normally the ItemData values are all zero.  However, if you assign a non-zero value to an item in your listbox (.SetItemData method if memory serves me well), you can use the .GetItemData to navigate to any item in the listbox.  You would only assign a value if the directory was not found.  It would be at the same place in your code where you highlight the item.
0
 
LVL 31

Accepted Solution

by:
Marco Gasi earned 0 total points
ID: 39989087
Solved. My code was wrong: that's incredible, uh!? ;-)

This code i wrong:

      for I := 0 to slOriginal.Count - 1 do
      begin
        rx.Subject := slOriginal[I];
        if rx.Match then
        begin
          lbxRegFile.Items.Add(slOriginal[I]); // adds item only if holds a direcotry
          if (not DirectoryExists(ExtractFilePath(rx.MatchedText))) then
          begin

Open in new window


This is right:

      for I := 0 to slOriginal.Count - 1 do
      begin
        lbxRegFile.Items.Add(slOriginal[I]); //adss each item and then tests if directory exists
        rx.Subject := slOriginal[I];
        if rx.Match then
        begin
          if (not DirectoryExists(rx.MatchedText)) then
          begin

Open in new window


That said, what about ItemData?
0
 
LVL 46

Expert Comment

by:aikimark
ID: 39989202
Are you using the Tlistbox or TCustomlistbox?  I looked for a code example and it seems that the GetItemdata() method is only available when you derived from the TCustomListbox class.  So, this might be a 'never mind' situation.

I have played with the regular expression pattern and the (registry slice) text file you posted.  It wasn't clear from that playing if your pattern is giving you what you want.  Part of that confusion is my ignorance about which lines you need to check.
0
 
LVL 46

Expert Comment

by:aikimark
ID: 39989294
Do you need to find the paths that are part of the registry key text?
Example:
[HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\12.0\C:\Users\marqus]

What are you looking for when the text contains multiple paths?
Example:
[HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\12.0\C:\Users\marqus\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\C:\Users\Marco\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup\ Options]
and
$(BDSLIB)\$(Platform)\release;$(BDSUSERDIR)\Imports;$(BDS)\Imports;$(BDSCOMMONDIR)\Dcp;$(BDS)\include;ProgramFlsRaize\CS5\Lib\RS-XE3\Win32;C:\RAD Components\Delphi\Jedi XE3\jcl\jcl\lib\d17\win32;C:\RAD Components\Delphi\Jedi XE3\jcl\jcl\source\include;C:\RAD Components\Delphi\Jedi XE3\jvcl\lib\D17\win32;C:\RAD Components\Delphi\Jedi XE3\jvcl\common;C:\RAD Components\Delphi\Jedi XE3\jvcl\Resources;C:\RAD Components\Delphi\DCPCryptLast;C:\RAD Components\Delphi\DCPCryptLast\Ciphers;C:\RAD Components\Delphi\DCPCryptLast\Hashes;C:\RAD Components\Delphi\MAX\mxProtector\Component;C:\RAD Components\Delphi\FoxControls;C:\MyProgramsUNI\Utils;C:\RAD Components\Delphi\RegEx\TPerlRegEx;C:\MyComponents\TmgDigiTimer\source;C:\MyComponents\TmgEasyLog\source;C:\MyComponents\TmgFileListator\source;C:\MyComponents\TmgWebUpdater\source;C:\RAD Components\Delphi\Abbrevia5_1\source;$(DXVCL)\Library\RS17;C:\RAD Components\Delphi\XMLParser;C:\RAD Components\Delphi\MAX\mxStorage\COMPONENT
and
"C:\Users\marqus\AppData\Local\Microsoft\Windows\C:\Users\Marco\AppData\Local\Microsoft\Windows\History\"="False"

=================
Or, what about text that includes a file name in the path?
Example:
TSourceModul'C:\RAD Components\Delphi\Jedi XE3\jcl\jcl\source\common\JclIDEUtils.pas',,
0
 
LVL 31

Author Comment

by:Marco Gasi
ID: 39989410
Take it easy, aikimark :-) In comment ID: 39989087 I said I solved the problem. For the specific issue which has made me open this thread the problem was a trivial error in my code.

Regex works correctly (I have to check another couple of thing and maybe I'll open a new question soon about that). In order to answer to your question, I need to get any incorrect path. I'll be happy to give you more details but this question is public and I would prefer a bit of privacy since I'm working on a commercial software. I don't know how we can speak privately, so if you can tell m, I'll be glad to do it.

About ItemData, I use a TListBox and in fact I saw that CodeCompletion didn't give me nor ItemData property nor GetItemData and SetItemData methods: now I know why :-)
0
 
LVL 46

Expert Comment

by:aikimark
ID: 39989437
No problem.  I'll stop my playing with this problem.  If you open a related question, please post a link to it in this thread.  Thanks.
0
 
LVL 31

Author Closing Comment

by:Marco Gasi
ID: 40001138
Now code is working.
0

Featured Post

Want to be a Web Developer? Get Certified Today!

Enroll in the Certified Web Development Professional course package to learn HTML, Javascript, and PHP. Build a solid foundation to work toward your dream job!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
The uses clause is one of those things that just tends to grow and grow. Most of the time this is in the main form, as it's from this form that all others are called. If you have a big application (including many forms), the uses clause in the in…
In this brief tutorial Pawel from AdRem Software explains how you can quickly find out which services are running on your network, or what are the IP addresses of servers responsible for each service. Software used is freeware NetCrunch Tools (https…
In this video, Percona Solution Engineer Dimitri Vanoverbeke discusses why you want to use at least three nodes in a database cluster. To discuss how Percona Consulting can help with your design and architecture needs for your database and infras…
Suggested Courses
Course of the Month7 days, 19 hours left to enroll

765 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question