Jump to content
mikerabat

SysUtils, AnsiString

Recommended Posts

Hi!

 

I'm currently migrating our projects from Delphi2010 to Delphi 10.4. One of my biggest

changes is actually using StrLCopy and other Ansistring functions that used to be in SysUtils.

They are now marked as deprecated and moved to System.AnsiStrings so ... I followed the compiler hint.

 

Now the base problem is that the compiler is confused which function to use so I always prefix the call

with

System.AnsiStrings.StrLCopy ...

which fixed that.

 

But that looks actually very "ugly" and I have the feeling that this is not the way to go idera ment to go.

 

Is there a "best" or anticipated way to handle this?

 

kind regards

  Mike

 

Share this post


Link to post
1 hour ago, mikerabat said:

Is there a "best" [...] way to handle this?

Yes, ditch AnsiString.

  • Like 6

Share this post


Link to post

You could create your own function MyStrLCopy() which simply call the original one. If you make it inline, then it will be as fast.

Share this post


Link to post
16 hours ago, Stefan Glienke said:

Yes, ditch AnsiString.

haha...

I'd love to but I have file and communication structures that need good old ansi strings.

 

Or do you actually have a better idea than:
str8 := UTF8String(SurName);
System.AnsiStrings.StrLCopy(@patData.surname[0],PAnsiChar(str8), length(patData.surname));

 

to bring a normal delphi string (SurName) to a structure (patData) that contains single byte characters?

 

 

 

Share this post


Link to post
Posted (edited)
1 hour ago, mikerabat said:

bring a normal delphi string (SurName) to a structure (patData) that contains single byte characters?

TEncoding.UTF8.GetBytes

 

 

1 hour ago, mikerabat said:

I have file and communication structures that need good old ansi strings.

You don't need to use AnsiString.

Edited by David Heffernan

Share this post


Link to post
4 minutes ago, David Heffernan said:

TEncoding.UTF8.GetBytes

 

 

You don't need to use AnsiString.

You mean I don't need AnsiCharacters to communicate with a device that only understands Single Byte characters?

 

So... how is that?

 

And tell me then how to use GetBytes to fill a structure like:

 

type

  TPatData = packed record

     SurName : Array[0..31] of AnsiChar;

     FirstName : Array[0..31] of AnsiChar;

  end;

 

var pat : TPatData;

      formSurName : String;

begin

        FillChar(pat, sizeof(pat), 0);

       

       // code to be filled here....

 

end;

 

Share this post


Link to post
9 minutes ago, mikerabat said:

You mean I don't need AnsiCharacters to communicate with a device that only understands Single Byte characters?

 

So... how is that?

TEncoding.UTF8.GetBytes

Share this post


Link to post
23 minutes ago, David Heffernan said:

TEncoding.UTF8.GetBytes

Sorry but can you elaborate that? I actually cannot see how I get there a string converted to a field in the record...

what am I missing here?

Share this post


Link to post

That returns a UTF-8 encoded byte array which you can copy into your record. Are you familiar with TEncoding, it's a somewhat import tool for text encodings?

Share this post


Link to post
Posted (edited)
9 hours ago, mikerabat said:

Sorry but can you elaborate that? I actually cannot see how I get there a string converted to a field in the record...

what am I missing here?

The main problem with TEncoding.GetBytes() is that its 'public' overloads all require the output to be a TBytes array, which you would then have to copy into your record afterwards, eg:

var
  patData : TPatData;
  SurName : String;
  Bytes: TBytes;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  Bytes := TEncoding.UTF8.GetBytes(SurName);
  Move(PBytes(Bytes)^, patData.SurName[0], Math.Min(SizeOf(patData.SurName), Length(Bytes)));
  ...
end;

The one overload of GetBytes() that would actually let you output directly into your record without using TBytes is declared as 'strict protected', which means you can't use it without involving some hacks, eg:

type
  TEncodingHelper = class(TEncoding)
  public
    function GetBytes(const S: string; Bytes: PBytes; ByteCount: Integer): Integer;
  end;

function TEncodingHelper.GetBytes(const S: string; Bytes: PBytes; ByteCount: Integer): Integer;
begin
  Result := GetBytes(PChar(S), Length(S), Bytes, ByteCount);
end;

var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  TEncodingHelper(TEncoding.UTF8).GetBytes(S, PByte(@patData.SurName[0]), SizeOf(patData.SurName));
  ...
end;

Or:

type
  TEncodingHelper = class helper for TEncoding
  public
    function GetBytes(const S: string; Bytes: PByte; ByteCount: Integer): Integer;
  end;

function TEncodingHelper.GetBytes(const S: string; Bytes: PByte; ByteCount: Integer): Integer;
begin
  Result := Self.GetBytes(PChar(S), Length(S), Bytes, ByteCount);
end;

var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  TEncoding.UTF8.GetBytes(S, PByte(@patData.SurName[0]), SizeOf(patData.SurName));
  ...
end;

The alternative would be to use System.LocaleCharsFromUnicode() instead, eg:

var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  LocaleCharsFromUnicode(CP_UTF8, 0, PChar(SurName), Length(SurName), @patData.SurName[0], sizeof(patData.SurName), nil, nil);
  ...
end;

 

Edited by Remy Lebeau

Share this post


Link to post
57 minutes ago, Remy Lebeau said:

... The one overload of GetBytes() that would actually let you output directly into your record without using TBytes is declared as 'strict protected', ...

It sounds like a Muprhy's Law: the most useful methods are usualy declared as private.

  • Haha 1

Share this post


Link to post
On 6/25/2021 at 9:06 PM, Remy Lebeau said:

The main problem with TEncoding.GetBytes() is that its 'public' overloads all require the output to be a TBytes array, which you would then have to copy into your record afterwards, eg:


var
  patData : TPatData;
  SurName : String;
  Bytes: TBytes;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  Bytes := TEncoding.UTF8.GetBytes(SurName);
  Move(PBytes(Bytes)^, patData.SurName[0], Math.Min(SizeOf(patData.SurName), Length(Bytes)));
  ...
end;

The one overload of GetBytes() that would actually let you output directly into your record without using TBytes is declared as 'strict protected', which means you can't use it without involving some hacks, eg:


type
  TEncodingHelper = class(TEncoding)
  public
    function GetBytes(const S: string; Bytes: PBytes; ByteCount: Integer): Integer;
  end;

function TEncodingHelper.GetBytes(const S: string; Bytes: PBytes; ByteCount: Integer): Integer;
begin
  Result := GetBytes(PChar(S), Length(S), Bytes, ByteCount);
end;

var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  TEncodingHelper(TEncoding.UTF8).GetBytes(S, PByte(@patData.SurName[0]), SizeOf(patData.SurName));
  ...
end;

Or:


type
  TEncodingHelper = class helper for TEncoding
  public
    function GetBytes(const S: string; Bytes: PByte; ByteCount: Integer): Integer;
  end;

function TEncodingHelper.GetBytes(const S: string; Bytes: PByte; ByteCount: Integer): Integer;
begin
  Result := Self.GetBytes(PChar(S), Length(S), Bytes, ByteCount);
end;

var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  TEncoding.UTF8.GetBytes(S, PByte(@patData.SurName[0]), SizeOf(patData.SurName));
  ...
end;

The alternative would be to use System.LocaleCharsFromUnicode() instead, eg:


var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  LocaleCharsFromUnicode(CP_UTF8, 0, PChar(SurName), Length(SurName), @patData.SurName[0], sizeof(patData.SurName), nil, nil);
  ...
end;

 

Thank you very much much for the great insight!

Share this post


Link to post

For a long time ago (it is not the beginning of a fairy tale :classic_biggrin: ) I have been working with communication systems of various types and when I started to collide with unicode (and the various problems at the time), I started using Indy utilities for data management (even if not necessarily coming from Indy components).
In particular, I was more than happy with the utilities and definitions in IdGlobal:
TIdBytes, RawToBytes, BytesTo ...... (there is also BytesToString).
Until now they have always taken me out of any hindrance in handling byte data.

I hope I was helpful.

Greetings

Share this post


Link to post
Posted (edited)
34 minutes ago, DelphiUdIT said:

In particular, I was more than happy with the utilities and definitions in IdGlobal:
TIdBytes, RawToBytes, BytesTo ...... (there is also BytesToString).

In that regard, Indy's IIdTextEncoding interface has a public GetBytes() method that would be useful in this example, eg:

uses
  ..., IdGlobal;

var
  patData : TPatData;
  SurName : String;
begin
  FillChar(@patData, sizeof(patData), 0);
  SurName := ...;
  IndyTextEncoding_UTF8.GetBytes(PChar(SurName), Length(SurName), PByte(@patData.SurName[0]), SizeOf(patData.SurName));
  ...
end;

 

Edited by Remy Lebeau

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×