views:

400

answers:

4

I've got a report that's supposed to take a grid control and produce HTML output. One of the columns in the grid can display any of a number of values, or <Any>. When this gets output to HTML, of course, it ends up blank.

I could probably write up some routine to use StringReplace to turn that into &lt;Any&gt; so it would display this particular case correctly, but I figure there's probably one in the RTL somewhere that's already been tested and does it right. Anyone know where I could find it?

+5  A: 
Andreas Rejbrand
+1  A: 

I usually just use this code:

function HTMLEncode(Data:string):string;
begin
  Result:=
    StringReplace(
    StringReplace(
    StringReplace(
    StringReplace(
    StringReplace(
      Data,
      '&','&amp;',[rfReplaceAll]),
      '<','&lt;',[rfReplaceAll]),
      '>','&gt;',[rfReplaceAll]),
      '"','&quot;',[rfReplaceAll]),
      #13#10,'<br />'#13#10,[rfReplaceAll]);
end;

(copyright? it's open source)

Stijn Sanders
This looks *much* slower than a simple loop: for i := 1 to length(Data) do case ord(Data[i]) of ...
Andreas Rejbrand
I just tested this: Nested StringReplace: 801259 ticks. A single loop: 532037 ticks.
Andreas Rejbrand
But of course, the *very simplest* looping approach will fail with the #13#10...
Andreas Rejbrand
hmm, interesting, could there still be some performance to be gained by using TStringStream?
Stijn Sanders
The main performance thief is probably the constant reallocation of the resulting string. This can be resolved as in da-soft's reply. But performance is not an issue at all for the OP, so, it's more of an interesting sidenote. :)
Andreas Rejbrand
Stijn Sanders
+2  A: 

It seems here is a small contest :) Here is a one more implementation:

function HTMLEncode3(const Data: string): string;
var
  iPos, i: Integer;

  procedure Encode(const AStr: String);
  begin
    Move(AStr[1], result[iPos], Length(AStr) * SizeOf(Char));
    Inc(iPos, Length(AStr));
  end;

begin
  SetLength(result, Length(Data) * 6);
  iPos := 1;
  for i := 1 to length(Data) do
    case Data[i] of
      '<': Encode('&lt;');
      '>': Encode('&gt;');
      '&': Encode('&amp;');
      '"': Encode('&quot;');
    else
      result[iPos] := Data[i];
      Inc(iPos);
    end;
  SetLength(result, iPos - 1);
end;

Update 1: Updated initially provided incorrect code.

Update 2: And the times:

HTMLEncode :   2286508597
HTMLEncode2:   3577001647
HTMLEncode3:    361039770
da-soft
This is a very nice solution! (Maybe a bit overkill, though! :) )The main gain is probably not the Move, but the fact that you do not need constantly to allocate more space for the result. I would give you a +1 unless it were for the fact that it is not an answer to the actual question! :)
Andreas Rejbrand
Well, I give you +1 just because it's a beautiful example of optimization.
Andreas Rejbrand
(By the way: you do know there is a subtle difference between a "content" and a "contest"? :) )
Andreas Rejbrand
Sure ! I have similar problem with "meet" and "meat" :)
da-soft
A: 

Unit HTTPApp has a function called HTMLEncode. It has also other HTML/HTTP related functions.

ErvinS
As have been pointed out long before...
Andreas Rejbrand