Link to home
Start Free TrialLog in
Avatar of ST3VO
ST3VOFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Find if String is missing in memo

Hi all,

In a memo I have some <li> tags that don't have it's closing tag and I need some code to look for those and close them.

This is how it works:

<li>somthing's go here or none...and then the next tag to the <li> is </li>

so, basically, if the tag after <li> is NOT an </li> then insert the </li> tag.

Please let me know if I didn't explain it properly.

hope you can help

thx

st3vo
ASKER CERTIFIED SOLUTION
Avatar of 8080_Diver
8080_Diver
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
Avatar of ST3VO

ASKER

Hmmm...how to I get your function to search a whole memo contents and add the missing </li> 's please?

Can't get it to work :o/

thx
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
SOLUTION
Avatar of Emmanuel PASQUIER
Emmanuel PASQUIER
Flag of France 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
Avatar of ST3VO

ASKER

OK, it aded the missing </li> but in the wrong place.

I had this:

<li img src="blabla" ...  

What the code did was ...

<li></li><img src="blabla" ...  

If should have done this:

</li><img src="blabla" alt="" /></li>

Another example:  

<li><u>hello</u>

should become:

<li><u>hello</u></li>

Does this help?





ha that is just another problem.
You must tell us what is the tag or piece of text that ALWAYS mark the position where </li> should be
If, as in your example, it's not any tag, then we have a problem
Avatar of ST3VO

ASKER

Same issue epasquier....it's adding the closing tag right after ... It might be more complez than expected :o/
Avatar of ST3VO

ASKER

Let's see if this helps:

Rules: The </li> tag just comes before an opening <li> tag OR a </ul> or a </ol> tag

So, the code should only add the closing </li> tag right before any of those other tags:

<li> OR </ul> OR </ol>

Usage:
<ol>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ol>

<ul>
  <li>Coffee</li>
  <li>Tea</li>
  <li>Milk</li>
</ul>

Hope this helps
Avatar of ST3VO

ASKER

So, if there is an <li><li> then before the last <li> we insert a closing </li> and so on...

Also if there is a </ol>  before the </ol> there must be a closing </li>

And last but not least  if there is a </ul>  before the </ol> there must be a closing </li>

And opening <li> needs a closing </li> before any other <li>  or </ol> or </ul>

Understand it?

Yes it helps, as I said the previous message it's another problem and we need to know what other tags we should expect.

I'll look into it
try this one :

DetectAndAddClosingTags( Memo1.Lines.Text , 'li' , [ 'li' , 'ol', 'ul' ] );
function GetPosOfFirstToken(aText:String;TokenList:TStrings;P:integer=1):integer;
Var
 i,P2:integer;
begin
 Result:=0;
 for i:=0 to TokenList.Count-1 do
  begin
   P2:=PosEx(aText,TokenList[i],P);
   if (P2>0) And ((Result=0) Or (P2<Result) Then Result:=P2;
  end; 
end;

function DetectAndAddClosingTags(aText:String;Tag:String='li';closeTags:Array of string):String;
Var
 OpenTag,CloseTag:String;
 LOpenTag,LCloseTag,i:Integer;
 OpenTokens,CloseTokens:TStrings;
 
begin
 OpenTokens:=TStringList.Create;
 CloseTokens:=TStringList.Create;
 
 Tag:=LowerCase(Tag);
 OpenTokens.Add('<'+Tag+'>');
 OpenTokens.Add(UpperCase(OpenTokens[0]));
 LOpenTag:=Length(OpenTag);
 CloseTag:='</'+Tag+'>';
 LCloseTag:=Length(CloseTag);
 
 for i:=Low(closeTags) to High(closeTags) do
  begin
   CloseTokens.Add('</'+LowerCase(closeTags[i])+'>');
   CloseTokens.Add('</'+UpperCase(closeTags[i])+'>');
  end;
 
 i:=1;
 Repeat
  i:=GetPosOfFirstToken(aText, OpenTokens, i);
  if i=0 Then break; // quit when none found
  i := i + LOpenTag;
  i := GetPosOfFirstToken(aText, CloseTokens, i);
  if (i = 0) then
   begin
    aText := aText + CloseTag;
    break;
   end else
  if LowerCase(Copy(aText,i,LCloseTag))<>CloseTag then
      Insert(CloseTag , aText, i);
 Until False; // "infinite" loop
 Result:=aText;
 OpenTokens.Free;
 CloseTokens.Free;
end;

Open in new window

ah, one mistake, it's not possible to have a default parameter in the middle of standard parameter

You can't declare this :
function DetectAndAddClosingTags(aText:String;Tag:String='li';closeTags:Array of string):String;

but have to declare this :
function DetectAndAddClosingTags(aText:String;Tag:String;closeTags:Array of string):String;
Avatar of ST3VO

ASKER

2nd function gives errors to compile:

[DCC Error] Unit1.pas(308): E2238 Default value required for 'closeTags'

and

[DCC Error] Unit1.pas(977): E2035 Not enough actual parameters

Any ideas pls?
Avatar of ST3VO

ASKER

Ignore the last error posted please...

The only error is:

[DCC Error] Unit1.pas(308): E2238 Default value required for 'closeTags'

2nd function ... line 1

Here: function DetectAndAddClosingTags(aText:String;Tag:String='li';closeTags:Array of string):String;

for the first error yes, it is what I corrected the message before
for the second, I don't know, I suspect you forgot a parameter :

DetectAndAddClosingTags( Memo1.Lines.Text , 'li' , [ 'li' , 'ol', 'ul' ] );
Avatar of ST3VO

ASKER

I don't mean the usage code...it's the 2nd function that doesn't compile and returns the error:

function DetectAndAddClosingTags(aText:String;Tag:String='li';closeTags:Array of string):String;
Var
 OpenTag,CloseTag:String;
 LOpenTag,LCloseTag,i:Integer;
 OpenTokens,CloseTokens:TStrings;
 
begin
 OpenTokens:=TStringList.Create;
 CloseTokens:=TStringList.Create;
 
 Tag:=LowerCase(Tag);
 OpenTokens.Add('<'+Tag+'>');
 OpenTokens.Add(UpperCase(OpenTokens[0]));
 LOpenTag:=Length(OpenTag);
 CloseTag:='</'+Tag+'>';
 LCloseTag:=Length(CloseTag);
 
 for i:=Low(closeTags) to High(closeTags) do
  begin
   CloseTokens.Add('</'+LowerCase(closeTags[i])+'>');
   CloseTokens.Add('</'+UpperCase(closeTags[i])+'>');
  end;
 
 i:=1;
 Repeat
  i:=GetPosOfFirstToken(aText, OpenTokens, i);
  if i=0 Then break; // quit when none found
  i := i + LOpenTag;
  i := GetPosOfFirstToken(aText, CloseTokens, i);
  if (i = 0) then
   begin
    aText := aText + CloseTag;
    break;
   end else
  if LowerCase(Copy(aText,i,LCloseTag))<>CloseTag then
      Insert(CloseTag , aText, i);
 Until False; // "infinite" loop
 Result:=aText;
 OpenTokens.Free;
 CloseTokens.Free;
end;


look above, the post 26101250 just below the code I provided
Avatar of ST3VO

ASKER

I know hwat you mean...: DetectAndAddClosingTags( Memo1.Lines.Text , 'li' , [ 'li' , 'ol', 'ul' ] );  = Function usage code...

That's cool...

Its just the function above that gets the error: [DCC Error] Unit1.pas(308): E2238 Default value required for 'closeTags' ... on it's first line...and I cannot compile the code because of the error, so I cannot test it...otherwise all cool!
argh :o)

Replace the function definition : you still have Tag:String='li'; where you should have Tag:String;
( without ='li' ) which is not allowed with other parameters after, when those do not have also default values. Which is why the compiler complains about not having default values for closeTags
Avatar of ST3VO

ASKER

Cool...got it to compile...but tested the code and it's not adding any </li> tags at all for some reason.
can you post your memo text so that I try too ?
Avatar of ST3VO

ASKER

sure

This is what I'm testing it with:

<ul class="list">
<li><img alt="picture" align="left" src="images/icon_1.jpg" width="30" height="30" />some text here just to test
</ul>


As you can see it's got a missing </li> before the </ul>

there was a few mistakes
1) PosEx order of parameters (substring comes before the total string) - I do that one regularly
2) I forgot to add the open tags (<li>) for possible delimiters to search after the tag is found
3) minor mistake as OpenTag was not saved then LOpenTag = 0

it works now
function GetPosOfFirstToken(aText:String;TokenList:TStrings;P:integer=1):integer;
Var
 i,P2:integer;
begin
 Result:=0;
 for i:=0 to TokenList.Count-1 do
  begin
   P2:=PosEx(TokenList[i],aText,P);
   if (P2>0) And ((Result=0) Or (P2<Result)) Then Result:=P2;
  end; 
end;

function DetectAndAddClosingTags(aText:String;Tag:String;closeTags:Array of string):String;
Var
 OpenTag,CloseTag:String;
 LOpenTag,LCloseTag,i:Integer;
 OpenTokens,CloseTokens:TStrings;
 
begin
 OpenTokens:=TStringList.Create;
 CloseTokens:=TStringList.Create;
 
 Tag:=LowerCase(Tag);
 OpenTag:='<'+Tag+'>';
 OpenTokens.Add(OpenTag);
 OpenTokens.Add(UpperCase(OpenTag));
 LOpenTag:=Length(OpenTag);
 CloseTag:='</'+Tag+'>';
 LCloseTag:=Length(CloseTag);

 for i:=Low(closeTags) to High(closeTags) do
  begin
   CloseTokens.Add('</'+LowerCase(closeTags[i])+'>');
   CloseTokens.Add('</'+UpperCase(closeTags[i])+'>');
  end;
// Add also the opentags
 CloseTokens.AddStrings(OpenTokens);

 i:=1;
 Repeat
  i:=GetPosOfFirstToken(aText, OpenTokens, i);
  if i=0 Then break; // quit when none found
  i := i + LOpenTag;
  i := GetPosOfFirstToken(aText, CloseTokens, i);
  if (i = 0) then
   begin
    aText := aText + CloseTag;
    break;
   end else
  if LowerCase(Copy(aText,i,LCloseTag))<>CloseTag then
      Insert(CloseTag , aText, i);
 Until False; // "infinite" loop
 Result:=aText;
 OpenTokens.Free;
 CloseTokens.Free;
end;

Open in new window

Avatar of ST3VO

ASKER

Hi epasquier,

Did ytou manage ro test it?

I just tested it and it does nothing for some reason....I've added your new code above too but nothing happens.

Any ideas? I'm I forgetting something?

thx
Yes, it worked for me, you probably missed something

I called it like that (result is put in a second memo)

 Memo2.Lines.Text := DetectAndAddClosingTags( Memo1.Lines.Text , 'li' , [ 'li' , 'ol', 'ul' ] );

double check the code and execute with my test text :
// MEMO1
<ol>
  <li>Coffee
  <li>Tea</li>
  <li>Milk<img src="blob.img">
</ol>

// RESULT IN MEMO2
<ol>
  <li>Coffee
  </li><li>Tea</li>
  <li>Milk<img src="blob.img">
</li></ol>

Open in new window

Hmmm...how to I get your function to search a whole memo contents and add the missing </li> 's please?

The assumption I made was that you would be passing whatever portion of the memo field had the list in it (i.e. the portion betwen the <ul> and </ul> or the <ol> and the </ol>).  To search the whole thing, you would need to pass the whole text as a string or modify the function to use TMemo objects..  
Can't get it to work :o/
Well, I didn't test the function since I was at work and being paid to do work things. ;-)  
Re: your issues with not finding "<li" and changing it to "<li>"
As others have stated, this is an entirely differnt problem from the originally stated one.  What this is indicating is that your memo field's entire list is totally malformed.  THat changes the whole picture because what you are saying is that the process has to determine a) whether there is, in fact, some indication of a list item start and then b) if there was, is there some indication of an end of list item.  That means you need to parse for the "less than" symbol to see if you have a potentially start of tag and, then, b), assuming you find it, determine if it is a malformed "<li>", c) correcting any malformations of that tag before d) searching for the next "less than" symbol so that you can e) try to determine whether it is a 1) a potentially malformed "end of list item", 2) a well formed "end of list item", or 3) some other tag, in which case if it is either a well-formed or malformed "start of list item", you will need to remediate the preceding substring to add an "end of list item" tag.
Just as a passing question, is it at all possible that there will be lists within a list?  (I frequently wind up doing that in HTML ;-)?  If so, then you cannot assume that a "Less Than" symbol is either the start or end of a list item and you may need to deal with other malformed tags, as well.  For instance, if you have:
<ol><li some text <ul><li>some more text</li<li>still more text</li></li<lianother line of text</ol
Do you plan to remediate that text so that it is properly formed, as below?
<ol><li> some text <ul><li>some more text</li><li>still more text</li><ul></li<li>another line of text</li></ol>
 
Avatar of ST3VO

ASKER

epasquier: Yes, it works on a second memo BUT I need to run the code on only one memo and it doesn't work when I specify just one memo...very strange.

8080_Diver: Thanks for you comment and yes you are right, the LI 's can go even more complex but I didn't want the code to become a nightmare to create so I just asked for some code that could do the basics for now.





>> Yes, it works on a second memo BUT I need to run the code on only one memo
>> and it doesn't work when I specify just one memo...very strange.

That's more than strange, that's unbelievable. It works for 1 memo just as well, if it does not for you then you have something special with your memo, in other part of your project
Avatar of ST3VO

ASKER

I'll investigate further and let you know..thanks again!
ST3VO,
Can you provide the two memo fields texts in file attachments?
Avatar of ST3VO

ASKER

It was a false alarm...I though it worked that the last code I cannot get to work...sorry about that...It's just to adding the </li>

You might be asking yourselfs why so many string replaces? Well, I'll tell you as you might have a better solution for this.

I have some html page which I'm loading on twebbrower and then i'm putting the tbrowser on designmode ... when I look into the code after putting the browser on design mode the html code just brakes up completely so I am trying to get it back to normal by fixing the things it breaks (html code wise) ... any ideas here please???

thx

Just out of curiosity, why are you [putting the TWebBrowser in design mode and then trying to look at the code you loaded into it?  
Why not look at the code from the HTML file, instead?
Avatar of ST3VO

ASKER

Good question but I am trying to use TWebBrowser as an editor and it works BUT as soon as you switch to design mode the code changes to the worse html code I've ever seen to I am trying to fix that by doing replaces everywhere :o/
Instead of trying to use the TWebBrowser for something for which it was never intended (i.e. as an HTML Editor), try looking at the following link (which is for an html editor ;-):
http://www.chami.com/html-kit/ 
Avatar of ST3VO

ASKER

Looks promising but can I use it in Delphi? and does it work in design mode?

thnks

ST3VO,
Please tell me that you are NOT trying to set the contents of the TWebBrowser in Design Mode with the intention of having whatever you set in design mode be what is displayed.  I highly advise the use of text files of type "HTM" or"HTML" to store the HTML code that you want to display in the TWebBrowser rather than trying to put the code directly into (much less actually trying to edit the HTML code in) the TWebBrowser component.
No, HTML-Kit cannot be used "in Delphi" and, therefore, it does not "work in design mode".  t is a tool for editing/creating HTML files that you can then load (programmatically) into the TWebBrowser.  Loading HTML into the TWebBrowser control is a much better approach because you don't have to edit and recompile your application every time you adjust the HTML code.
Avatar of ST3VO

ASKER

Ref: Please tell me that you are NOT trying to set the contents of the TWebBrowser in Design Mode ... Yes, that's what I've trying to do. :o/

Why?!?!?!?
Avatar of ST3VO

ASKER

Right...this question has gone way off the original question so I'm going to close it and later ask about the editor :o)
Avatar of ST3VO

ASKER

Thanks for your time and help!!!
Avatar of ST3VO

ASKER

Why not  :o)
'Cause it don't work all that well. ;-)
As an English friend of mine often says, "Horses for courses."  In other words, there are tools that are better for some things than for others.  
Another quote that comes to mind i:
"To a man with a hammer, every problem looks like a nail."
If your intent is to work harder at getting something done, then you would seem to be on the right course.  However, I tend to prefer using tools that are designed ofr a purpose rather than trying to force tools to do things they were never designed for.  My approach tends to let me get more done faster rather than bashing my head on a wall trying to figure out how to turn a screw with a hammer. ;-)
Avatar of ST3VO

ASKER

Hahahaha...yeah you are right...I just like to try to not like when things cannot be done so I turn to try to convert a hummer into a screw driver :o)