Strings in Elements vs Delphi Code

I converted a lot of my Delphi code and most of that worked quite nicely.

But now I have to deal with the code which works with strings to parse files and such.

I am using arrays of Byte which can easily read from Streams and converted to AnsiStrings and the code looks quite ok. My approach tries to make the code compilable with Delphi so there are quite a lot of IFDEFS.

But while working with strings I found those not consistent as expected:

AnsiString is missing a Compare function and also a SubString function.
I cannot assign an AnsiString to a String easily and vice versa. Especially for file parsing it would be completely ok, if only the ASCII codepage would be copied unless a code page can be specified.

DelphiStrings also miss a lot of the String functions but on the other hand can be parameter to the good old Compare() function. AnsiStrings do not work here.

The assignment
var S2 : AnsiString := Copy(S,1,7);

seems to raise an exception if S does not have enough characters. In Delphi Copy would not cause a problem - the length will be truncated to the maximum count.

Would it be possible to make the string types and functions more interchangeable?

Can Copy() be fixed to not raise an exception?

Thanks, logged as bugs://78935

Agreed, that shouldn’t fail.

I have another one:

var S3 : AnsiString := ‘text’;
if S3 = ‘text’ then

fails with an index error.

The problem is here

operator AnsiString.Equal(Value1: AnsiString; Value2: AnsiString): Boolean;
begin

var i := 0;
while (Value1.Chars[i] = Value2.Chars[i]) and (i < Value1.Length) do
inc(i);

“(i < Value1.Length)” should be executed first.

Hi Julian,
thanks for your report, taking a look right now to your issues.

Hi,
the Copy function has been fixed (also remove, that had the same issue).
AnsiString now has SubString function.
These fixes are in GitHub and in the next beta.

About assigning AnsiString <-> DelphiString I’m going to take a look now.
Also, please, which functionality / functions you miss in DelphiString type?
Thanks a lot.

Thanks for taking care of this -
What I missed was:

Compare DelphiString to AnsiString
Compare DelphiString to String
( also the = and <> operators, would that work? )

I also missed the SetLength function which can shorten a string or add #0 placeholders at the end.

Also ToIntegerDef is used very often.

Thanks again!

1 Like

ok, will add SetLength and ToIntegerDef.

About comparing AnsiString with DelphiString (and operators) will work, but first need to know what is stored in the bytes. We can add add a field to AnsiString, like the CodePage, supporting now ASCII, UTF8, UTF16LE and UTF16BE.
Is this ok?

Thanks a lot for your comments.

I don’t think this element would be necessary. I understand the idea behind it, but for the cases I would use an AnsiString I would only need to compare the lower 7 bits of the byte with the lower 7 bits of the unicode character. But that comparison should be as fast as possible.

If I need special characters I can still convert the AnsiString to a String (or DelphiString) while providing a code page.

Delphi is doing an on-the-fly conversion using the current codepage which kind of works, but causes problems when a program is run at a different locale. Having an additional field in the AnsiString a bit contradict this type to be small and effective. If for each comparison the string is decoded I expect it will seriously slow down parsing logic.

The Stream has a method ReadBytes(Count: LongInt): TBytes; - I use that to read an AnsiString from a file. Is there something similar to read a String or DelphiString from a file?

Ok, I see, let’s keep simple then, without conversions on the fly that can slow the comparison operations.

About a read method for String / DelphiString there isn’t right now a method, I’m going to add and will be available for next beta a couple of TStream methods:
method ReadString(Count: LongInt; aEncoding: TEncoding = Unicode): Delphistring
method WriteString(Bytes: array of Byte; aEncoding: TEncoding = Unicode):LongInt

Thanks!

That sounds great - many thanks!

Found a small problem in AnsiString - The SetLength function fails if the new length is smaller than the old length. This is the fix:

method AnsiString.SetLength(aLength: Integer);

if aLength>Length then aLength := Length;
for i: Integer := 0 to aLength - 1 do

BTW.: DelphiString is missing SetLength function.

… I know, I should not use SetLength at all and use a StringBuilder instead. Will do that when I optimize the code …

1 Like

I fixed right now, also added ReadString and WriteString method.

Will check in DelphiString.SetLength tomorrow.

Thanks a lot!

1 Like

Thank You -

I have a variable:
FWriteReplacement: AnsiString;

Which is not assigned by default. Now when I compare it against ‘’ which is ok in Delphi I get a System.NullReferenceException. Is this expected?

I added this method to be used for property Length and it fixes that issue

method AnsiString.GetLength : Integer;
begin
if (Self=nil) or (fData=nil) then exit 0 else exit fData.length;
end;

property Length: Integer read GetLength write SetLength;

I understand this behavior is intended for the standard String type, but I am not sure for AnsiString since that is part of the Delphi RTL.

The standard String type, btw, is missing the method IsNullOrEmpty since “Length=0” also does not work on null strings.

No, sounds like a bug in out comparison operator. We’ll have a look.

Yes, good catch, we’ll review.

Thanks!

1 Like

Fixed AnsiString.GetLength for uninitialized instances.

Thanks for reporting!

Have more :slight_smile:
The + operator fails if the first string is not initialized.

I included these changes to make it work.

constructor AnsiString(Value: PlatformString; AsUTF16Bytes: Boolean := false);
begin
if (Value=nil) or (Value.Count=0) then exit;

method AnsiString.Insert(aIndex: Integer; aValue: AnsiString): AnsiString;
begin
if fData=nil then
begin fData := aValue.Data; exit; end;

method AnsiString.Insert(aIndex: Integer; Value: AnsiChar): AnsiString;
begin
if fData=nil then
begin fData := new Byte[1]; fData[0] := Byte(Value); exit; end;

Thanks!

ok, I’m going to check all AnsiStrting operations with not initialized.

Thanks!!

I can not reproduce this one, please. could you post an small test case and platform?

Ok, I can not reproduce the issue because the get_length is fixed, so when the ansiString is not initialized, returns 0 as length.
But for y.Insert(0, x) still fails (when y is not initialized), so fixing!

Thanks!!