Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2016-07

Weird string behavior

37 messages · 9 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Santiago A.

Hello:

I'm working on windows XP, FPC 3.0.0 from stable Lazarus 1.6.

I've come across this issue: When I concatenate two strings in UTF8 they are converted to ansi (Win-1252) .
A bug?
Am I missing something?

I have attached a demo.

Saludos Santiago A.
Bart
to Santiago A.
Santiago A. wrote
I've come across this issue: When I concatenate two strings in UTF8 they are converted to ansi (Win-1252) .

You have declared all string variables as plain "string", which is the same as AnsiString(CP_ACP). So all string variables have the encoding of your active codepage.

Declare Utf8StrA and related as Utf8String.
In DisplayBytes do not use "String" as parametertype, since this will again automatically convert things.
The AnsiToUtf8 is not necessary anymore if done this way:

procedure DisplayBytes(S:RawByteString);
var
  i:Integer;
begin
  Write('  ');
  for i:=1 to length(s) do
    write(ord(s[i]),' ');
  writeln;
end;
//-----------------------------------
// body
//-----------------------------------
var
  AnsiStrA:string;
  AnsiStrB:string;
  Utf8StrA: utf8string;
  Utf8StrB:utf8string;
  Utf8StrConcat:utf8string;
begin
  AnsiStrA:=' ';
  AnsiStrA[1]:=#243; // o acute win-1252
  AnsiStrB:='A';
  Write('AnsiStrA: ');DisplayBytes(AnsiStrA); // 243
  Write('AnsiStrB: ');DisplayBytes(AnsiStrB); // 65
  Utf8StrA:=(AnsiStrA); // 195 179
  Utf8StrB:=(AnsiStrB); // 65
  writeln;
  Write('Utf8StrA: ');DisplayBytes(Utf8StrA); // 195 179
  Write('Utf8StrB: ');DisplayBytes(Utf8StrB); // 65
  Write('Utf8StrA+Utf8StrB: ');DisplayBytes(Utf8StrA+Utf8StrB);
  writeln;
  Write('Utf8StrA again: ');DisplayBytes(Utf8StrA); // 195 179
  Write('Utf8StrB again: ');DisplayBytes(Utf8StrB); // 65
  Utf8StrConcat:=Utf8StrA+Utf8StrB;
  writeln;
  Write('Utf8StrConcat: ');DisplayBytes(Utf8StrConcat);
end.

Bart

Santiago A.
to Bart
Bart wrote 12 lines
On 7/21/16, Santiago A. wrote: > I've come across this issue: When I concatenate two strings in UTF8 they > are converted to ansi (Win-1252) . You have declared all string variables as plain "string", which is the same as AnsiString(CP_ACP). So all string variables have the encoding of your active codepage. Declare Utf8StrA and related as Utf8String. In DisplayBytes do not use "String" as parametertype, since this will again automatically convert things. The AnsiToUtf8 is not necessary anymore if done this way:
var
  AnsiStrA:string;  // AnsiString(CP_ACP)
  AnsiStrB:string;  // AnsiString(CP_ACP)
  Utf8StrA: string; // AnsiString(CP_ACP)
  Utf8StrB: string; // AnsiString(CP_ACP)
  Utf8StrConcat:string; // AnsiString(CP_ACP)
begin
  AnsiStrA:=' ';
  AnsiStrA[1]:=#243; // o acute win-1252
  AnsiStrB:='A';
  // AnsiStrA is AnsiString(CP_ACP)
  // AnsiStrB is AnsiString(CP_ACP)
  Utf8StrA:=AnsiToUtf8(AnsiStrA); // 195 179
  Utf8StrB:=AnsiToUtf8(AnsiStrB); // 65
  // is Utf8StrA now utf8string? or something similar like Ansistring(UTF_8)
  // is Utf8StrB now utf8string? or something similar like Ansistring(UTF_8)
  Utf8StrConcat:=Utf8StrA+Utf8StrB;
  //  AnsiString(CP_ACP) = UTF8 + UT8
  //  automatic Conversion to ansiString(CP_ACP) ?
end;
Saludos Santi
Bart
to Santiago A.
Santiago A. wrote
// is Utf8StrA now utf8string? or something similar like Ansistring(UTF_8) // is Utf8StrB now utf8string? or something similar like Ansistring(UTF_8)

Just check the vaue of StringCodePage(Utf8StrA).

procedure DisplayBytes(S:RawByteString);
var
  i:Integer;
begin
  Write('  ');
  for i:=1 to length(s) do
    write(ord(s[i]),' ');
  writeln;
end;
var
  AnsiStrA: String;
begin
  AnsiStrA:=' ';
  AnsiStrA[1]:=#243; // o acute win-1252
  AnsiStrA := AnsiToUtf8(AnsiStrA);
  writeln('StringCodePage(AnsiStrA) now is: ',stringcodepage(ansistra));
  Write('AnsiStrA: ');DisplayBytes(AnsiStrA);
end.

Gives:
StringCodePage(AnsiStrA) now is: 65001
AnsiStrA: 195 179

Notice that your original problem was mainly due to the fact that DisplayBytes used a String parameter, which lead to automatically converting everything back to your Windows codepage.

Bart

Graeme Geldenhuys
to Bart
Bart wrote
Gives: StringCodePage(AnsiStrA) now is: 65001 AnsiStrA: 195 179

I don't understand, why did AnsiStrA change its codepage type after the 3rd assignment to it?

Here is the results on my Windows 7 system.

==========================================
$ codepagestring.exe
StringCodePage(AnsiStrA) now is: 1252
StringCodePage(AnsiStrA) now is: 65001
AnsiStrA: 195 179
==========================================

All I did to the last code example was duplicate the line of code that calls stringcodepage() as shown here... So I get a before and after result.

==========================================
program codepagestring;
{$mode objfpc}{$H+}
procedure DisplayBytes(S:RawByteString);
var
  i:Integer;
begin
  Write('  ');
  for i:=1 to length(s) do
    write(ord(s[i]),' ');
  writeln;
end;
var
  AnsiStrA: String;
begin
  writeln('StringCodePage(AnsiStrA) now is: ',stringcodepage(ansistra));
  AnsiStrA:=' ';
  AnsiStrA[1]:=#243; // o acute win-1252
  AnsiStrA := AnsiToUtf8(AnsiStrA);
  writeln('StringCodePage(AnsiStrA) now is: ',stringcodepage(ansistra));
  Write('AnsiStrA: ');
  DisplayBytes(AnsiStrA);
end.
==========================================

Regards,
Graeme

fpGUI Toolkit - a cross-platform GUI toolkit using Free Pascal http://fpgui.sourceforge.net/ My public PGP key: http://tinyurl.com/graeme-pgp
Santiago A.
to Bart: Just check the vaue of StringCodePage(Utf8StrA). procedure DisplayBytes(S:RawByteString);…
Bart wrote
Just check the vaue of StringCodePage(Utf8StrA).

Not Initialized
AnsiStrA: 1252
ResultA: 1252

AnsiStrA:=' '
  AnsiStrA: 0
AnsiStrA[1]:=#243; // o acute win-1252
  AnsiStrA: 0
ResultA:=AnsiStrA
  ResultA: 0
ResultA := AnsiStrA + ' '
  ResultA: 1252
ResultA:=AnsiToUtf8(AnsiStrA);
  ResultA: 65001
ResultA:= AnsiToUtf8(AnsiStrA) + AnsiToUtf8(AnsiStrA);
  ResultA: 1252

I'm definitively completely lost

-------------------------------

program testconvertstr;
var
  AnsiStrA:string;
  ResultA:string;
begin
  writeln('Not Initialized');
  writeln('  AnsiStrA: ',stringcodepage(ansistra));
  writeln('  ResultA: ',stringcodepage(ResultA));
  Writeln;writeln('AnsiStrA:='' ''');
  AnsiStrA:=' ';
  writeln('  AnsiStrA: ',stringcodepage(ansistra));
  Writeln;writeln('AnsiStrA[1]:=#243; // o acute win-1252');
  AnsiStrA[1]:=#243; // o acute win-1252
  writeln('  AnsiStrA: ',stringcodepage(ansistra));
  Writeln;writeln('ResultA:=AnsiStrA');
  ResultA:=AnsiStrA;
  writeln('  ResultA: ',stringcodepage(ResultA));
  Writeln;writeln('ResultA := AnsiStrA + '' ''');
  ResultA:=AnsiStrA+' ';
  writeln('  ResultA: ',stringcodepage(ResultA));
  Writeln;Writeln('ResultA:=AnsiToUtf8(AnsiStrA);');
  ResultA:=AnsiToUtf8(AnsiStrA);
  writeln('  ResultA: ',stringcodepage(ResultA));
  Writeln;writeln('ResultA:= AnsiToUtf8(AnsiStrA) + AnsiToUtf8(AnsiStrA);');
  ResultA:=AnsiToUtf8(AnsiStrA)+AnsiToUtf8(AnsiStrA);
  writeln('  ResultA: ',stringcodepage(ResultA));
  Readln;
end.
Saludos Santi
Jonas Maebe
to Santiago A.: Not Initialized AnsiStrA: 1252 ResultA: 1252 AnsiStrA:=' ' AnsiStrA: 0 AnsiStrA[1]:=#243;…
Santiago A. wrote
program testconvertstr;

You are missing {$h+} here. When posting programs, always include all switches and/or all command line options. The program also compiles with string = shortstring (the default), but has different behaviour in that case.

Show quoted text 7 lines
var AnsiStrA:string; ResultA:string; begin writeln('Not Initialized'); writeln(' AnsiStrA: ',stringcodepage(ansistra)); writeln(' ResultA: ',stringcodepage(ResultA));

The string code page of an empty string is always DefaultSystemCodePage.

Writeln;writeln('AnsiStrA:='' '''); AnsiStrA:=' '; writeln(' AnsiStrA: ',stringcodepage(ansistra));

The string code page of constant strings is described at http://wiki.freepascal.org/FPC_Unicode_support#String_constants . In this case, it is CP_ACP (= 0) because no source file code page has been set.

Writeln;writeln('AnsiStrA[1]:=#243; // o acute win-1252'); AnsiStrA[1]:=#243; // o acute win-1252 writeln(' AnsiStrA: ',stringcodepage(ansistra));

Changing an individual byte of a string has no influence on its code page.

Writeln;writeln('ResultA:=AnsiStrA'); ResultA:=AnsiStrA; writeln(' ResultA: ',stringcodepage(ResultA));

Assigning a ansistring to another ansistring with the same declared code page (both AnsiStrA and ResultA have CP_ACP as declared code page) won't change the (dynamic) string code page (see
http://wiki.freepascal.org/FPC_Unicode_support#Dynamic_code_page ).

Writeln;writeln('ResultA := AnsiStrA + '' '''); ResultA:=AnsiStrA+' '; writeln(' ResultA: ',stringcodepage(ResultA));

See http://wiki.freepascal.org/FPC_Unicode_support#String_concatenation : the result of a string concatenation will always be converted to the declared code page of the destination (and CP_ACP represents the current value of DefaultSystemCodePage, see
http://wiki.freepascal.org/FPC_Unicode_support#Code_page_identifiers ).

Writeln;Writeln('ResultA:=AnsiToUtf8(AnsiStrA);'); ResultA:=AnsiToUtf8(AnsiStrA); writeln(' ResultA: ',stringcodepage(ResultA));

AnsiToUtf8() returns a RawByteString with dynamic code page CP_UTF8 (so that the dynamic code page matches the actual string encoding). Assigning a RawByteString to any other string type never results in a string code page conversion (see
http://wiki.freepascal.org/FPC_Unicode_support#RawByteString ).

Writeln;writeln('ResultA:= AnsiToUtf8(AnsiStrA) + AnsiToUtf8(AnsiStrA);'); ResultA:=AnsiToUtf8(AnsiStrA)+AnsiToUtf8(AnsiStrA); writeln(' ResultA: ',stringcodepage(ResultA));

See again
http://wiki.freepascal.org/FPC_Unicode_support#String_concatenations (same as before).

Jonas

Jonas Maebe
to Santiago A.
Santiago A. wrote 9 lines
So ResultA := AnsiToUTF8(AnsiStrA + AnsiStrA); // UTF-8 ResultB := AnsiToUTF8(AnsiStrA) + AnsiToUTF8(AnsiStrA); // Win-1252 And resultA is not equal to ResultB It doesn't look like too intuitive.

It would be good if someone with access to Delphi 2009+ could test this. It is possible that the result of concatenating two RawByteStrings should again be a RawByteString and that hence no conversion should happen on assignment here either.

I would say that it is closer to "hidden secret knowledge" than to the "Principle of least surprise".

There is no hidden secret knowledge. Everything is documented and the information is linked from the release notes. The "principle of least surprise" has been applied in the sense that we didn't invent our own system that introduces small or large differences compared to how Delphi behaves in the same situation (and if there are differences, then those are bugs in our implementation).

Jonas

Santiago A.
to Jonas Maebe
Jonas Maebe wrote 7 lines
There is no hidden secret knowledge. Everything is documented and the information is linked from the release notes. The "principle of least surprise" has been applied in the sense that we didn't invent our own system that introduces small or large differences compared to how Delphi behaves in the same situation (and if there are differences, then those are bugs in our implementation).

With hidden knowledge I don't mean it's not documented, I mean that's like small print in contracts.

Only those who know there is trick with adding are going to check manual. Complex things and pushing the language to its limits could require reading manual, adding two strings shouldn't.

MyString := expression

I really shouldn't need to know if right expression uses '+', or the result of a function, or '*', to guess what MyString type is.

In addition, changing the codepage on the fly if a bad idea. If I cant change the codepage dynamically (I don't like it, but let's live with it), let me assign it explicitly, don't change it on the fly.

SetCodePage(MyString,win1252);
MyString := AnsiToUTF8(Ansi1 + Ansi2);  // Automatically converted to
Win-1252 before assign
MyString := AnsiToUTF8(Ansi1) + AnsiToUTF8(Ansi2);   // Automatically
converted to Win-1252 before assign
SetCodePage(MyString,utf8);
MyString := AnsiToUTF8(Ansi1 + Ansi2);  //   No conversion needed
MyString := AnsiToUTF8(Ansi2) + AnsiToUTF8(Ansi2); // No conversion needed
MyString := Ansi1 + Ansi2;  //  Automatically converted to Utf8 before
assign

None changes the codepage of the String but me

I don't like automatic conversion, but let's live with it. But I think that automatic change of var type is really wrong.

This is Pascal, not bash or PHP.

Saludos Santiago A.
Graeme Geldenhuys
to Santiago A.
Santiago A. wrote
In addition, changing the codepage on the fly if a bad idea.

+1
I think that is very much the wrong behaviour too. As I mentioned before is this mailing list. I think the FPC 3.x with these codepage aware AnsiStrings is a damn mess. As far as I can see, FPC is now worse that Delphi 2009 was. And NO, don't tell me everything should work as it did before... It doesn't!! This thread just proves that one more time.

Regards,
Graeme

Jonas Maebe
to Santiago A.: With hidden knowledge I don't mean it's not documented, I mean that's like small print in…
Santiago A. wrote 11 lines
El 22/07/2016 a las 17:56, Jonas Maebe escribió: > > There is no hidden secret knowledge. Everything is documented and the > information is linked from the release notes. The "principle of least > surprise" has been applied in the sense that we didn't invent our own > system that introduces small or large differences compared to how > Delphi behaves in the same situation (and if there are differences, > then those are bugs in our implementation). With hidden knowledge I don't mean it's not documented, I mean that's like small print in contracts.

How much more prominent can it be when it's in the release notes?

Only those who know there is trick with adding are going to check manual. Complex things and pushing the language to its limits could require reading manual, adding two strings shouldn't.

You don't have to read the manual. You have to read the release notes.

MyString := expression I really shouldn't need to know if right expression uses '+', or the result of a function, or '*', to guess what MyString type is.

And maybe you shouldn't, because maybe there is a bug in FPC. But I don't have Delphi 2009 and hence I cannot test.

In addition, changing the codepage on the fly if a bad idea. If I cant change the codepage dynamically (I don't like it, but let's live with it), let me assign it explicitly, don't change it on the fly.

You can do that if you only use RawByteString. I doubt you'll find it very convenient.

I don't like automatic conversion, but let's live with it. But I think that automatic change of var type is really wrong.

I don't understand what you mean with this.

Jonas

Bart
to Jonas Maebe: It would be good if someone with access to Delphi 2009+ could test this. It is possible…
Jonas Maebe wrote 9 lines
> ResultA := AnsiToUTF8(AnsiStrA + AnsiStrA); // UTF-8 ResultB := > AnsiToUTF8(AnsiStrA) + AnsiToUTF8(AnsiStrA); // Win-1252 > > > And resultA is not equal to ResultB > > It doesn't look like too intuitive. It would be good if someone with access to Delphi 2009+ could test this.

I asked on Dutch Delphi forum if someone could test with D2009 or up.

Bart

Petr Kohut
to Bart

Hello,
I tested the program listed below in "Delphi 10.1 Berlin" with the results shown in the comments.

PK

program Project3;
{$APPTYPE CONSOLE}
{$R *.res}
var
   AnsiStrA: AnsiString;
   ResultA: AnsiString;
begin
   Writeln('Not Initialized');
   Writeln('  AnsiStrA: ', StringCodePage(AnsiStrA)); // 1250
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 1250
   Writeln;
   Writeln('AnsiStrA := '' ''');
   AnsiStrA := ' ';
   Writeln('  AnsiStrA: ', StringCodePage(AnsiStrA)); // 1250
   Writeln;
   Writeln('AnsiStrA[1] := #243; // o acute win-1252');
   AnsiStrA[1] := #243; // o acute win-1252
   Writeln('  AnsiStrA: ', StringCodePage(AnsiStrA)); // 1250
   Writeln;
   Writeln('ResultA := AnsiStrA');
   ResultA := AnsiStrA;
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 1250
   Writeln;
   Writeln('ResultA := AnsiStrA + '' ''');
   ResultA := AnsiStrA + ' ';
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 1250
   Writeln;
   Writeln('ResultA := AnsiToUtf8(AnsiStrA);');
   ResultA := AnsiToUtf8(AnsiStrA);
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 65001
   Writeln;
   Writeln('ResultA := AnsiToUtf8(AnsiStrA) + AnsiToUtf8(AnsiStrA);');
   ResultA := AnsiToUtf8(AnsiStrA) + AnsiToUtf8(AnsiStrA);
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 65001
   Writeln;
   Writeln('ResultA := AnsiToUtf8(AnsiStrA) + AnsiStrA;');
   ResultA := AnsiToUtf8(AnsiStrA) + AnsiStrA;
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 65001
   Writeln;
   Writeln('ResultA := AnsiStrA + AnsiToUtf8(AnsiStrA);');
   ResultA := AnsiStrA + AnsiToUtf8(AnsiStrA);
   Writeln('  ResultA: ', StringCodePage(ResultA));   // 1250
   Readln;
end.

(*
Not Initialized
AnsiStrA: 1250
ResultA: 1250

AnsiStrA := ' '
   AnsiStrA: 1250
AnsiStrA[1] := #243; // o acute win-1252
   AnsiStrA: 1250
ResultA := AnsiStrA
   ResultA: 1250
ResultA := AnsiStrA + ' '
   ResultA: 1250
ResultA := AnsiToUtf8(AnsiStrA);
   ResultA: 65001
ResultA := AnsiToUtf8(AnsiStrA) + AnsiToUtf8(AnsiStrA);
   ResultA: 65001
ResultA := AnsiToUtf8(AnsiStrA) + AnsiStrA;
   ResultA: 65001

ResultA := AnsiStrA + AnsiToUtf8(AnsiStrA);
ResultA: 1250
*)

------ Původní zpráva ------
Od: "Bart"
Komu: "FPC-Pascal users discussions"
Odesláno: 23.07.2016 0:29:32
Předmět: Re: [fpc-pascal] Weird string behavior

Show quoted text 19 lines
On 7/22/16, Jonas Maebe wrote: >> ResultA := AnsiToUTF8(AnsiStrA + AnsiStrA); // UTF-8 ResultB := >> AnsiToUTF8(AnsiStrA) + AnsiToUTF8(AnsiStrA); // Win-1252 >> >> >> And resultA is not equal to ResultB >> >> It doesn't look like too intuitive. > > It would be good if someone with access to Delphi 2009+ could test >this. I asked on Dutch Delphi forum if someone could test with D2009 or up. Bart _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Mattias Gaertner
to Bart: I asked on Dutch Delphi forum if someone could test with D2009 or up. Bart
Show quoted text 13 lines
On 7/22/16, Jonas Maebe wrote: >> ResultA := AnsiToUTF8(AnsiStrA + AnsiStrA); // UTF-8 ResultB := >> AnsiToUTF8(AnsiStrA) + AnsiToUTF8(AnsiStrA); // Win-1252 >> >> >> And resultA is not equal to ResultB >> >> It doesn't look like too intuitive. > > It would be good if someone with access to Delphi 2009+ could test this. I asked on Dutch Delphi forum if someone could test with D2009 or up.

Here is a result of Delphi 10.1:

program DTestConcatenate;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils;
var
  s1,s2,s3: AnsiString;
  r1,r2,r3: RawByteString;
begin
  s1:='A';   // 1252
  s2:='Aä';  // 1252
  writeln('s1="',s1,'" cp=',StringCodePage(s1));
  writeln('s2="',s1,'" cp=',StringCodePage(s2));
  r1:=AnsiToUTF8(s1); // 65001
  r2:=AnsiToUTF8(s2); // 65001
  writeln('r1="',r1,'" cp=',StringCodePage(r1));
  writeln('r2="',r2,'" cp=',StringCodePage(r2));
  r3:=r1+r2; // 65001
  writeln('r3="',r3,'" cp=',StringCodePage(r3));
  s3:=r1+r2; // 65001
  writeln('s3="',s3,'" cp=',StringCodePage(s3));
end.

Mattias

Jonas Maebe
to Mattias Gaertner
Mattias Gaertner wrote
Here is a result of Delphi 10.1:

Thank you (also Petr). Maybe one more, to know what happens if you mix rawbytestring and ansistring in the concatenation:

program DTestConcatenate;
{$APPTYPE CONSOLE}
{$R *.res}
uses
   System.SysUtils;
var
   s1,s2,s3: AnsiString;
   r1,r2,r3: RawByteString;
begin
   s1:='A';   // 1252
   s2:='Aä';  // 1252
   writeln('s1="',s1,'" cp=',StringCodePage(s1));
   writeln('s2="',s1,'" cp=',StringCodePage(s2));
   r1:=AnsiToUTF8(s1); // 65001
   r2:=AnsiToUTF8(s2); // 65001
   writeln('r1="',r1,'" cp=',StringCodePage(r1));
   writeln('r2="',r2,'" cp=',StringCodePage(r2));
   r3:=s1+r2; // ??
   writeln('r3="',r3,'" cp=',StringCodePage(r3));
   r3:=r1+s2; // ??
   writeln('r3="',r3,'" cp=',StringCodePage(r3));
   s3:=s1+r2; // ??
   writeln('r3="',r3,'" cp=',StringCodePage(r3));
   s3:=r1+s2; // ??
   writeln('r3="',r3,'" cp=',StringCodePage(r3));
end.

Jonas

Jonas Maebe
to Jonas Maebe
Jonas Maebe wrote 38 lines
On 23/07/16 08:11, Mattias Gaertner wrote: > Here is a result of Delphi 10.1: Thank you (also Petr). Maybe one more, to know what happens if you mix rawbytestring and ansistring in the concatenation: program DTestConcatenate; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; var s1,s2,s3: AnsiString; r1,r2,r3: RawByteString; begin s1:='A'; // 1252 s2:='Aä'; // 1252 writeln('s1="',s1,'" cp=',StringCodePage(s1)); writeln('s2="',s1,'" cp=',StringCodePage(s2)); r1:=AnsiToUTF8(s1); // 65001 r2:=AnsiToUTF8(s2); // 65001 writeln('r1="',r1,'" cp=',StringCodePage(r1)); writeln('r2="',r2,'" cp=',StringCodePage(r2)); r3:=s1+r2; // ?? writeln('r3="',r3,'" cp=',StringCodePage(r3)); r3:=r1+s2; // ?? writeln('r3="',r3,'" cp=',StringCodePage(r3)); s3:=s1+r2; // ?? writeln('r3="',r3,'" cp=',StringCodePage(r3)); s3:=r1+s2; // ?? writeln('r3="',r3,'" cp=',StringCodePage(r3));

Some copy paste errors in the last four lines, that should be

   s3:=s1+r2; // ??
   writeln('s3="',s3,'" cp=',StringCodePage(s3));
   s3:=r1+s2; // ??
   writeln('s3="',s3,'" cp=',StringCodePage(s3));

And maybe also (to check whether there is a difference depending on the actual code page):

   setstringcodepage(rawbytestring(s1),65001,true);
   r3:=s1+r2; // ??
   writeln('r3="',r3,'" cp=',StringCodePage(r3));
   r3:=r1+s1; // ??
   writeln('r3="',r3,'" cp=',StringCodePage(r3));
   s3:=s1+r2; // ??
   writeln('s3="',s3,'" cp=',StringCodePage(s3));
   s3:=r1+s2; // ??
   writeln('s3="',s3,'" cp=',StringCodePage(s3));

Jonas

Unknown author
to Jonas Maebe: Thank you (also Petr). Maybe one more, to know what happens if you mix rawbytestring and…

On 07/23/2016 06:13 AM, Jonas Maebe wrote:
[...]

Show quoted text 8 lines
var s1,s2,s3: AnsiString; r1,r2,r3: RawByteString; begin s1:='A'; // 1252 s2:='Aä'; // 1252 writeln('s1="',s1,'" cp=',StringCodePage(s1)); writeln('s2="',s1,'" cp=',StringCodePage(s2));
writeln('s2="',s2,'" cp=',StringCodePage(s2));

you're not the only one to have missed that...

gotta wonder how fubar the test results are now ;)

NOTE: No off-list assistance is given without prior approval. *Please keep mailing list traffic on the list* unless private contact is specifically requested and granted.
Jonas Maebe
to Unknown author
Show quoted text 15 lines
On 07/23/2016 06:13 AM, Jonas Maebe wrote: [...] > var > s1,s2,s3: AnsiString; > r1,r2,r3: RawByteString; > begin > s1:='A'; // 1252 > s2:='Aä'; // 1252 > writeln('s1="',s1,'" cp=',StringCodePage(s1)); > writeln('s2="',s1,'" cp=',StringCodePage(s2)); writeln('s2="',s2,'" cp=',StringCodePage(s2)); you're not the only one to have missed that...

The only thing that matters for this test is the stringcodepage value, which is the correct one.

Jonas

Petr Kohut
to Jonas Maebe

Hello,
here are results:

...
begin
   Writeln('--------------');
   Writeln;
   s1 := 'A';   // 1250
   s2 := 'Aä';  // 1250
   Writeln('s1 = "', s1, '" cp = ', StringCodePage(s1));
   Writeln('s2 = "', s2, '" cp = ', StringCodePage(s2));
   r1 := AnsiToUTF8(s1); // 65001
   r2 := AnsiToUTF8(s2); // 65001
   Writeln('r1 = "', r1, '" cp = ', StringCodePage(r1));
   Writeln('r2 = "', r2, '" cp = ', StringCodePage(r2));
   r3 := s1 + r2; // 1250
   Writeln('r3 = "', r3, '" cp = ', StringCodePage(r3));
   r3 := r1 + s2; // 65001
   Writeln('r3 = "', r3, '" cp = ', StringCodePage(r3));
   s3 := s1 + r2; // 1250
   Writeln('s3 = "', s3, '" cp = ', StringCodePage(s3));
   s3 := r1 + s2; // 65001
   Writeln('s3 = "', s3, '" cp = ', StringCodePage(s3));
   SetCodePage(RawByteString(s1), 65001, True);
   r3 := s1 + r2; // 65001
   Writeln('r3="', r3, '" cp=', StringCodePage(r3));
   r3 := r1 + s2; // 65001
   Writeln('r3="', r3, '" cp=', StringCodePage(r3));
   s3 := s1 + r2; // 65001
   Writeln('s3="', s3, '" cp=', StringCodePage(s3));
   s3 := r1 + s2; // 65001
   Writeln('s3="', s3, '" cp=', StringCodePage(s3));
   Readln;
end.
(*
--------------

s1 = "A" cp = 1250
s2 = "Aä" cp = 1250
r1 = "A" cp = 65001
r2 = "Aä" cp = 65001
r3 = "AAä" cp = 1250
r3 = "AA?" cp = 65001
s3 = "AAä" cp = 1250
s3 = "AA?" cp = 65001
r3="AAä" cp=65001
r3="AA?" cp=65001
s3="AAä" cp=65001
s3="AA?" cp=65001

*)

------ Původní zpráva ------
Od: "Jonas Maebe"
Komu: "FPC-Pascal users discussions"
Odesláno: 23.07.2016 13:03:33
Předmět: Re: [fpc-pascal] Weird string behavior

Show quoted text 25 lines
On 23/07/16 12:58, wrote: > On 07/23/2016 06:13 AM, Jonas Maebe wrote: > [...] >> var >> s1,s2,s3: AnsiString; >> r1,r2,r3: RawByteString; >> begin >> s1:='A'; // 1252 >> s2:='Aä'; // 1252 >> writeln('s1="',s1,'" cp=',StringCodePage(s1)); >> writeln('s2="',s1,'" cp=',StringCodePage(s2)); > > writeln('s2="',s2,'" cp=',StringCodePage(s2)); > > > you're not the only one to have missed that... The only thing that matters for this test is the stringcodepage value, which is the correct one. Jonas _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Jonas Maebe
to Petr Kohut
Petr Kohut wrote
Hello, here are results:

Thanks a lot. Could you test one more? I think I will have all information I need then.

Jonas

{$APPTYPE CONSOLE}
type
   tcp866 = type ansistring(866);
var
   s1, s2, s3: tcp866;
begin
   s1:='abc';
   setcodepage(rawbytestring(s1),65001,false);
   s2:='def';
   setcodepage(rawbytestring(s2),437,false);
   s3:=s1+s2;
   Writeln('DefaultSystemCodePage = ',DefaultSystemCodePage);
   Writeln('s3 = "', s3, '" cp = ', StringCodePage(s3));
   Readln;
end.
Mattias Gaertner
to Jonas Maebe
Show quoted text 26 lines
On 23/07/16 13:31, Petr Kohut wrote: > Hello, > here are results: Thanks a lot. Could you test one more? I think I will have all information I need then. Jonas {$APPTYPE CONSOLE} type tcp866 = type ansistring(866); var s1, s2, s3: tcp866; begin s1:='abc'; setcodepage(rawbytestring(s1),65001,false); s2:='def'; setcodepage(rawbytestring(s2),437,false); s3:=s1+s2; Writeln('DefaultSystemCodePage = ',DefaultSystemCodePage); Writeln('s3 = "', s3, '" cp = ', StringCodePage(s3)); Readln; end.

DefaultSystemCodePage = 1252
s3 = "abcdef" cp = 65001

Mattias

Jonas Maebe
to Mattias Gaertner
Mattias Gaertner wrote
DefaultSystemCodePage = 1252 s3 = "abcdef" cp = 65001

Thanks. So the rule for concatenation appears to be:
* the dynamic code page of the result of a string concatenation is that of the left operand (except if it's an empty string, then it's that of the right operand)
* the declared code page of the final concatenation result is that of the left operand

You then process the assignment as if you are assigning a string with the above declared/dynamic code page to whatever you are assigning the result of the concatenation to (which means no code page conversion in case the declared code pages match, like in the above case).

That's indeed not what FPC does currently. It's mainly complicated by the fact that FPC contains optimised helpers to avoid the final assignment if possible and directly concatenate into the destination when possible (those helpers right now only get passed the declared code page of the final destination, and not that of the concatenation, and hence virtually always convert the result to the dynamic code page of the destination at this time -- there are some exceptions for rawbytestring)

Jonas

Mattias Gaertner
to Jonas Maebe
Show quoted text 10 lines
On 25/07/16 23:07, Mattias Gaertner wrote: > DefaultSystemCodePage = 1252 > s3 = "abcdef" cp = 65001 Thanks. So the rule for concatenation appears to be: * the dynamic code page of the result of a string concatenation is that of the left operand (except if it's an empty string, then it's that of the right operand) * the declared code page of the final concatenation result is that of the left operand

Here are some more hints:

{$APPTYPE CONSOLE}
type
   tcp866 = type ansistring(866);
var
   s1, s2: tcp866;
   u1: UTF8String;
   r1: RawByteString;
begin
   s1:='abc';
   setcodepage(rawbytestring(s1),65001,false);
   Writeln('s1 = "', s1, '" cp = ', StringCodePage(s1));
   u1:='nop';
   Writeln('u1 = "', u1, '" cp = ', StringCodePage(u1));
   s2:=s1+u1;
   Writeln('s2 = "', s2, '" cp = ', StringCodePage(s2));
   s2:=u1+s1;
   Writeln('s2 = "', s2, '" cp = ', StringCodePage(s2));
   r1:=s1+u1;
   Writeln('r1 = "', r1, '" cp = ', StringCodePage(r1));
   r1:=u1+s1;
   Writeln('r1 = "', r1, '" cp = ', StringCodePage(r1));
   readln;
end.

s1 = "abc" cp = 65001
u1 = "nop" cp = 65001
s2 = "abcnop" cp = 866
s2 = "nopabc" cp = 866
r1 = "abcnop" cp = 1252
r1 = "nopabc" cp = 1252

Mattias

Jonas Maebe
to Mattias Gaertner

Mattias Gaertner wrote on Tue, 26 Jul 2016:

Show quoted text 11 lines
On Mon, 25 Jul 2016 23:23:23 +0200 Jonas Maebe wrote: > Thanks. So the rule for concatenation appears to be: > * the dynamic code page of the result of a string concatenation is that > of the left operand (except if it's an empty string, then it's that of > the right operand) > * the declared code page of the final concatenation result is that of > the left operand Here are some more hints:

Could you try the same program with u1 as plain ansistring instead of utf8string? (with an additional
"setcodepage(rawbytestring(u1),65001,false);" after assigning u1)

Thanks,

Jonas

Mattias Gaertner
to Jonas Maebe
[...] Could you try the same program with u1 as plain ansistring instead of utf8string? (with an additional "setcodepage(rawbytestring(u1),65001,false);" after assigning u1)

Sure:

{$APPTYPE CONSOLE}
type
   tcp866 = type ansistring(866);
var
   s1, s2: tcp866;
   u1: UTF8String;
   r1: RawByteString;
   a1, a2: AnsiString;
begin
   s1:='cp866';
   setcodepage(rawbytestring(s1),65001,false);
   Writeln('s1 = "', s1, '" cp = ', StringCodePage(s1));
   a1:='acp';
   setcodepage(rawbytestring(a1),65001,false);
   Writeln('a1 = "', a1, '" cp = ', StringCodePage(a1));
   u1:='utf8';
   Writeln('u1 = "', u1, '" cp = ', StringCodePage(u1));
   s2:=s1+u1;
   Writeln('s2:=s1+u1 = "', s2, '" cp = ', StringCodePage(s2));
   s2:=u1+s1;
   Writeln('s2:=u1+s1 = "', s2, '" cp = ', StringCodePage(s2));
   r1:=s1+u1;
   Writeln('r1:=s1+u1 = "', r1, '" cp = ', StringCodePage(r1));
   r1:=u1+s1;
   Writeln('r1:=u1+s1 = "', r1, '" cp = ', StringCodePage(r1));
   a2:=s1+u1;
   Writeln('a2:=s1+u1 = "', a2, '" cp = ', StringCodePage(a2));
   a2:=u1+s1;
   Writeln('a2:=u1+s1 = "', a2, '" cp = ', StringCodePage(a2));
   s2:=s1+a1;
   Writeln('s2:=s1+a1 = "', s2, '" cp = ', StringCodePage(s2));
   s2:=a1+s1;
   Writeln('s2:=a1+s1 = "', s2, '" cp = ', StringCodePage(s2));
   r1:=s1+a1;
   Writeln('r1:=s1+a1 = "', r1, '" cp = ', StringCodePage(r1));
   r1:=a1+s1;
   Writeln('r1:=a1+s1 = "', r1, '" cp = ', StringCodePage(r1));
   a2:=s1+a1;
   Writeln('a2:=s1+a1 = "', a2, '" cp = ', StringCodePage(a2));
   a2:=a1+s1;
   Writeln('a2:=a1+s1 = "', a2, '" cp = ', StringCodePage(a2));
   readln;
end.
s1 = "cp866" cp = 65001
a1 = "acp" cp = 65001
u1 = "utf8" cp = 65001
s2:=s1+u1 = "cp866utf8" cp = 866
s2:=u1+s1 = "utf8cp866" cp = 866
r1:=s1+u1 = "cp866utf8" cp = 1252
r1:=u1+s1 = "utf8cp866" cp = 1252
a2:=s1+u1 = "cp866utf8" cp = 1252
a2:=u1+s1 = "utf8cp866" cp = 1252
s2:=s1+a1 = "cp866acp" cp = 866
s2:=a1+s1 = "acpcp866" cp = 866
r1:=s1+a1 = "cp866acp" cp = 1252
r1:=a1+s1 = "acpcp866" cp = 1252
a2:=s1+a1 = "cp866acp" cp = 1252
a2:=a1+s1 = "acpcp866" cp = 1252

It seems the Delphi rules for non rawbytestrings are:
- Concatenate two same declared strings: append bytes, copy dyn. cp from left operand. Declared cp of result is left operand.
- Assign same declared: no conversion, only refcount.
- Concatenate two different declared strings: convert both to UnicodeString and append. Maybe there is an optimization for same dyn cp.
- Assign different declared strings: convert to LHS.

Mattias

Jonas Maebe
to Mattias Gaertner

Mattias Gaertner wrote on Tue, 26 Jul 2016:

It seems the Delphi rules for non rawbytestrings are: - Concatenate two same declared strings: append bytes, copy dyn. cp from left operand. Declared cp of result is left operand.

Are you sure it's "append bytes" here and not "append bytes if same dyn cp, otherwise convert to unicodestring, concatenate, and convert back to dyn cp of left operand"?

Jonas

Mattias Gaertner
to Jonas Maebe
Show quoted text 9 lines
Mattias Gaertner wrote on Tue, 26 Jul 2016: > It seems the Delphi rules for non rawbytestrings are: > - Concatenate two same declared strings: append bytes, copy dyn. cp > from left operand. Declared cp of result is left operand. Are you sure it's "append bytes" here and not "append bytes if same dyn cp, otherwise convert to unicodestring, concatenate, and convert back to dyn cp of left operand"?

Now I'm sure. See attachments.

Mattias

Santiago A.
to Mattias Gaertner
Mattias Gaertner wrote
a3:=a1+a2 => cp = 1252 a3:=a2+a1 => cp = 65001

Is that the expected behavior?

IMHO the result should be the same. And the only way is to make it depend on a3, no matter what is in the left side. That's the way things are done in Pascal

Saludos Santi
Michael Van Canneyt
to Santiago A.
Santiago A. wrote 8 lines
El 26/07/2016 a las 12:27, Mattias Gaertner escribió: > a3:=a1+a2 => cp = 1252 > a3:=a2+a1 => cp = 65001 Is that the expected behavior? IMHO the result should be the same. And the only way is to make it depend on a3, no matter what is in the left side. That's the way things are done in Pascal

This is not correct. In pascal the right-hand side of an assignment has a well-defined type. The compiler checks whether the type on the right is assignment-compatible to the left side.

Michael.

Jonas Maebe
to Santiago A.: Is that the expected behavior? IMHO the result should be the same. And the only way is to…

Santiago A. wrote on Tue, 26 Jul 2016:

Show quoted text 8 lines
El 26/07/2016 a las 12:27, Mattias Gaertner escribió: > a3:=a1+a2 => cp = 1252 > a3:=a2+a1 => cp = 65001 Is that the expected behavior? IMHO the result should be the same. And the only way is to make it depend on a3, no matter what is in the left side. That's the way things are done in Pascal

String concatenations are not commutative operations, even if no code pages are involved (even in Pascal). I think it is logical that the code page of the left operand is kept, because it is the "base string" to which you add other data.

The fact that the concatenated data is not converted in this scenario makes some sense in the context of the "same declared code page -> no conversion" rule that is also used for assignments, but it's something I'm less happy about. Maintaining multiple helpers, compiler behaviours and documentation is even less enticing though, so I will implement the Delphi behaviour. It will also slightly speed up string concatenations when all strings have the same declared code page, because no checks need to be performed at run time regarding whether or not data needs to be converted.

Jonas

Santiago A.
to Michael Van Canneyt: This is not correct. In pascal the right-hand side of an assignment has a well-defined…
Michael Van Canneyt wrote 16 lines
On Tue, 26 Jul 2016, Santiago A. wrote: > El 26/07/2016 a las 12:27, Mattias Gaertner escribió: >> a3:=a1+a2 => cp = 1252 >> a3:=a2+a1 => cp = 65001 > Is that the expected behavior? > > IMHO the result should be the same. And the only way is to make it > depend on a3, no matter what is in the left side. That's the way things > are done in Pascal This is not correct. In pascal the right-hand side of an assignment has a well-defined type. The compiler checks whether the type on the right is assignment-compatible to the left side.

Sorry I meant no matter what is in the right side. Other way my statment has no sense ;-)

Show quoted text 7 lines
Michael. _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/cgi-bin/mailman/listinfo/fpc-pascal
Saludos Santi
Michael Van Canneyt
to Santiago A.
Santiago A. wrote 19 lines
El 26/07/2016 a las 16:19, Michael Van Canneyt escribió: > > > On Tue, 26 Jul 2016, Santiago A. wrote: > >> El 26/07/2016 a las 12:27, Mattias Gaertner escribió: >>> a3:=a1+a2 => cp = 1252 >>> a3:=a2+a1 => cp = 65001 >> Is that the expected behavior? >> >> IMHO the result should be the same. And the only way is to make it >> depend on a3, no matter what is in the left side. That's the way things >> are done in Pascal > > This is not correct. In pascal the right-hand side of an assignment > has a well-defined type. The compiler checks whether the type on the > right is assignment-compatible to the left side. Sorry I meant no matter what is in the right side. Other way my statment has no sense ;-)

Well, I'm also not sure what left/right sides you are referring to:

left/right of := or left/right of +

But as long as Jonas is on top of things, all will be well =-)

Michael.

Michael Schnell
to Michael Van Canneyt: This is not correct. In pascal the right-hand side of an assignment has a well-defined…
Michael Van Canneyt wrote
This is not correct. In pascal the right-hand side of an assignment has a well-defined type. The compiler checks whether the type on the right is assignment-compatible to the left side.

Hmm.

if you do

x := y + z;

with x a real and y and z integers, the type of x will not change to be an integer, but the value will be converted.

Now I understand that with strings the encoding is a kind of "sub-type" and hence (usually) static and not convertible to allow for the compiler do decide if a conversion is necessary.

This has been discussed a long time ago and the argument was that _fully_ dynamically typed strings are to costly regarding CPU demand.

I did not get to know that those design decision has been changed for the normal usage case (while there seems to be ways to sue certain kinds of strings in a fully dynamical way) .

Changing the encoding of the left side operand of ":=" would only be logical if the encoding is never an attribute to the string's type but always a dynamical attribute to the string's content.

-Michael.

Santiago A.
to Michael Schnell
Michael Schnell wrote 30 lines
On 07/26/2016 04:19 PM, Michael Van Canneyt wrote: > > This is not correct. In pascal the right-hand side of an assignment > has a well-defined type. The compiler checks whether the type on the > right is assignment-compatible to the left side. Hmm. if you do x := y + z; with x a real and y and z integers, the type of x will not change to be an integer, but the value will be converted. Now I understand that with strings the encoding is a kind of "sub-type" and hence (usually) static and not convertible to allow for the compiler do decide if a conversion is necessary. This has been discussed a long time ago and the argument was that _fully_ dynamically typed strings are to costly regarding CPU demand. I did not get to know that those design decision has been changed for the normal usage case (while there seems to be ways to sue certain kinds of strings in a fully dynamical way) . Changing the encoding of the left side operand of ":=" would only be logical if the encoding is never an attribute to the string's type but always a dynamical attribute to the string's content.

And what are the rules for changing left side operand? It looks that they are a little complicated.

Freepascal needed codepages, so string with codepage was needed. Should it need dynamic codepage for backward compatibility? INHO, no. String should had been an alias of rawbytestring, and "codepage aware strings" should be another new type, but codepage should be static.

Legacy programs could compile and run perfectly, and you could start using codepage aware strings type.

Automatic conversions? Well, I'm not for it, but any way, left side shouldn't change its codepage.

Nevertheless, that's my two cents. I looks that there is some pressure to be Delphi XX compatible, I left Delphi long long time ago (Delphi 5), so these compatibility issues are not in my radar.

Saludos Santiago A.
Michael Schnell
to Santiago A.
Santiago A. wrote
And what are the rules for changing left side operand?

Extremely hard to define when trying to follow any decent logic, unless the decision is either "never" (i.e. a strictly static typing) or "always" (a strictly dynamic typing).

A way out could be to set all "normal" string types to behave as strictly static typing (like any other types in Pascal) and a single dedicated (but intended to be used always where appropriate, e. g. in "TStrings") String type to behave as strictly dynamic typing).

Analyze and suggestions see ->
http://wiki.lazarus.freepascal.org/not_Delphi_compatible_enhancement_for_Unicode_Support

-Michael