Recent

Author Topic: IntToStr with national convention?  (Read 4867 times)

Milsa

  • Sr. Member
  • ****
  • Posts: 309
IntToStr with national convention?
« on: August 01, 2020, 04:49:29 pm »
I want convert number to  eye like format with national convenstions:

123456789 => '123 456 789' for SK language setting
123456789 => '123,456,789' for EN language setting

etc.

Conversion must be automatic by language selection. What command should I use?
I work with Lazarus 2.2.2, FPC 3.2.2, date 2022-05-15
This information is actual to: 28st Dec 2022

process_1

  • Guest
Re: IntToStr with national convention?
« Reply #1 on: August 01, 2020, 05:26:59 pm »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5476
  • Compiler Developer
Re: IntToStr with national convention?
« Reply #2 on: August 01, 2020, 05:32:34 pm »
Format as suggested by process_1 supports the thousand separator only for floating point numbers which might result in a precision loss.

Alternatively you can use the FmtBCD unit where you first convert your integer number to a tBCD (best by using the operator overloads) and then to a string by using BCDToStr with the FormatSettings overload. Correction. That does not work...  %)
« Last Edit: August 01, 2020, 05:35:40 pm by PascalDragon »

dsiders

  • Hero Member
  • *****
  • Posts: 1080
Re: IntToStr with national convention?
« Reply #3 on: August 01, 2020, 06:03:15 pm »
I want convert number to  eye like format with national convenstions:

123456789 => '123 456 789' for SK language setting
123456789 => '123,456,789' for EN language setting

etc.

Conversion must be automatic by language selection. What command should I use?

I use the following technique:

Code: Pascal  [Select][+][-]
  1.   {
  2.     var
  3.        i: Integer;
  4.        s: String;
  5.   }
  6.  
  7.   { just to prove use of current locale settings }
  8.   DefaultFormatSettings.ThousandSeparator := '~';
  9.  
  10.   i := 123456789;
  11.   s := FormatFloat('#,##0', i.ToDouble, DefaultFormatSettings);
  12.   // s := FormatFloat('#,##0', i.ToExtended, DefaultFormatSettings);
  13.   ShowMessage(s);
  14.  
Preview Lazarus 3.99 documentation at: https://dsiders.gitlab.io/lazdocsnext

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: IntToStr with national convention?
« Reply #4 on: August 01, 2020, 06:08:38 pm »
I want convert number to  eye like format with national convenstions:

123456789 => '123 456 789' for SK language setting
123456789 => '123,456,789' for EN language setting

etc.

Conversion must be automatic by language selection. What command should I use?
If you mean that the decimal/thousand separators must be selected automatic according to your language there is nothing to be done. All the string conversion functions use the correct separators according to your FormatSettings. (Note that you must add unit CLocale to uses on *nix).

If you mean that you have a multi-language program and you want to make the program use the separators according to the language selection of the user then you have a difficult task. A good solution exists only for Windows based on the LCID values. Have a look at the demo in the folder examples/translation of your Lazarus installation; unit "LocalizedForms" contains LCID related code. On other operating systems, however, you are out of luck (to my knowledge, which may be wrong), and you have to provide all the related tables yourself. In any case the FormatSettings with the appropriate separators must be added to the conversion routines as last parameter.

PascalDragon, wouldn't a locale-dependent table of the most important FormatSettings be a useful extension to SysUtils?
« Last Edit: August 01, 2020, 06:10:18 pm by wp »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5476
  • Compiler Developer
Re: IntToStr with national convention?
« Reply #5 on: August 01, 2020, 06:58:36 pm »
PascalDragon, wouldn't a locale-dependent table of the most important FormatSettings be a useful extension to SysUtils?

What is not important for one user might be important for the other. So that's not a good idea, not to mention that it shouldn't be our task to maintain these tables.

Instead one might extend the clocale unit with a similar function as the Windows' SysUtils.GetLocaleFormatSettings function if the OS supports it (the Android variant of clocale has GetAndroidFormatSettings for example).

process_1

  • Guest
Re: IntToStr with national convention?
« Reply #6 on: August 01, 2020, 07:00:39 pm »
If your integer number have up to, including 15 digits, you are safe from any rounding error.

Here is an example with format, even FormatFloat mentionied in some previous post  sometimes much more usefull:

Code: Pascal  [Select][+][-]
  1.  
  2. program Test;
  3. var
  4.   ts: char;
  5. begin
  6.      
  7.   ts := ThousandSeparator;
  8.    
  9.   ThousandSeparator := ',';
  10.   writeln(format('%0.n',[0.0]));
  11.   writeln(format('%0.n',[-123456789*1.0]));
  12.   writeln(format('%0.n',[123456789*1.0]));
  13.  
  14.   ThousandSeparator := ' ';
  15.   writeln(format('%0.n',[0.0]));
  16.   writeln(format('%0.n',[-123456789*1.0]));
  17.   writeln(format('%0.n',[123456789*1.0]));
  18.  
  19.   ThousandSeparator := ts;
  20.    
  21. end.
  22.  
  23.  

If you want full integer or int64 range to be supported, there is nothing you can do than make your own function have inttostr(), or int64tostr() and then manually insert separator on each third place inside string result.
« Last Edit: August 01, 2020, 07:10:47 pm by process_1 »

Milsa

  • Sr. Member
  • ****
  • Posts: 309
Re: IntToStr with national convention?
« Reply #7 on: August 01, 2020, 07:09:15 pm »
If you mean that you have a multi-language program and you want to make the program use the separators according to the language selection of the user then you have a difficult task. A good solution exists only for Windows based on the LCID values. Have a look at the demo in the folder examples/translation of your Lazarus installation; unit "LocalizedForms" contains LCID related code. On other operating systems, however, you are out of luck (to my knowledge, which may be wrong), and you have to provide all the related tables yourself. In any case the FormatSettings with the appropriate separators must be added to the conversion routines as last parameter.

PascalDragon, wouldn't a locale-dependent table of the most important FormatSettings be a useful extension to SysUtils?
Multi-language application with FormatSettings texts in PO file is solution too. Or?
I work with Lazarus 2.2.2, FPC 3.2.2, date 2022-05-15
This information is actual to: 28st Dec 2022

wp

  • Hero Member
  • *****
  • Posts: 11916
Re: IntToStr with national convention?
« Reply #8 on: August 01, 2020, 07:33:27 pm »
Good idea. You must declare resource string for all the strings used by the FormatSettings, and you must provide a routine which copies these translated strings to the currently used FormatSettings record.

On the other hand, it is an unnecessary burden for translators because all this information is available inside Windows and, as PascalDragon wrote, Android. I cannot imagine that locale information cannot be pulled out of Linux/Unix/macOS.

Bart

  • Hero Member
  • *****
  • Posts: 5290
    • Bart en Mariska's Webstek
Re: IntToStr with national convention?
« Reply #9 on: August 01, 2020, 11:06:23 pm »
Can't you just insert the DefaultFormatSettings.ThousandSeparator manually?
Rather crude (and slow, so plenty room for optimization):

Code: Pascal  [Select][+][-]
  1. function IntToStr(Value: UInt64; const TS: String): String; overload;
  2. begin
  3.   Str(Value, Result);
  4.   if (TS = '') then
  5.     Exit;
  6.   if (Value > 999) then
  7.     Insert(TS, Result, Length(Result)-2);
  8.   if (Value > 999999) then
  9.     Insert(TS, Result, Length(Result)-6);
  10.   if (Value > 999999999) then
  11.     Insert(TS, Result, Length(Result)-10);
  12.   if (Value > 999999999999) then
  13.     Insert(TS, Result, Length(Result)-14);
  14.   if (Value > 999999999999999) then
  15.     Insert(TS, Result, Length(Result)-18);
  16.   if (Value > 999999999999999999) then
  17.     Insert(TS, Result, Length(Result)-22);
  18. end;
  19.  
  20. function IntToStr(Value: Int64; const TS: String): String; overload;
  21. begin
  22.   Str(Value, Result);
  23.   if (TS = '') then
  24.     Exit;
  25.   if (Value > 999) or (Value < -999) then
  26.     Insert(TS, Result, Length(Result)-2);
  27.   if (Value > 999999) or (Value < -999999)then
  28.     Insert(TS, Result, Length(Result)-6);
  29.   if (Value > 999999999) or (Value < -999999999) then
  30.     Insert(TS, Result, Length(Result)-10);
  31.   if (Value > 999999999999) or (Value < -999999999999) then
  32.     Insert(TS, Result, Length(Result)-14);
  33.   if (Value > 999999999999999) or (Value < -999999999999999) then
  34.     Insert(TS, Result, Length(Result)-18);
  35.   if (Value > 999999999999999999) or (Value < -999999999999999999) then
  36.     Insert(TS, Result, Length(Result)-22);
  37. end;
  38.  
  39. var
  40.   S, Sep: String;
  41.   U: UInt64;
  42.   I: Int64;
  43. begin
  44.   Sep := DefaultFormatSettings.ThousandSeparator;
  45.   U := High(UInt64);
  46.   S := IntToStr(U,Sep);
  47.   writeln(U,' -> ',S);
  48.   I := High(Int64);
  49.   S := IntToStr(I,Sep);
  50.   writeln(I,' -> ',S);
  51.   I := Low(Int64);
  52.   S := IntToStr(I,Sep);
  53.   writeln(I,' -> ',S);
  54. end.

Outputs (Dutch locale):
Code: [Select]
C:\Users\Bart\LazarusProjecten\ConsoleProjecten>test
18446744073709551615 -> 18.446.744.073.709.551.615
9223372036854775807 -> 9.223.372.036.854.775.807
-9223372036854775808 -> -9.223.372.036.854.775.808

I made TS a string, because it allows to use UTF8 codepoints (hey, I'm on Lazarus).

Bart
« Last Edit: August 01, 2020, 11:32:24 pm by Bart »

Bart

  • Hero Member
  • *****
  • Posts: 5290
    • Bart en Mariska's Webstek
Re: IntToStr with national convention?
« Reply #10 on: August 02, 2020, 11:01:50 pm »
And that contained a bug.
This should work correctly:
Code: Pascal  [Select][+][-]
  1. function IntToStr(Value: UInt64; const TS: String): String; overload;
  2. var
  3.   TSLen: Integer;
  4. begin
  5.   Str(Value, Result);
  6.   if (TS = '') then
  7.     Exit;
  8.   TSLen := Length(TS);
  9.   if (Value > 999) then
  10.     Insert(TS, Result, Length(Result)-2);
  11.   if (Value > 999999) then
  12.     Insert(TS, Result, Length(Result)-5-TSLen);
  13.   if (Value > 999999999) then
  14.     Insert(TS, Result, Length(Result)-8-2*TSLen);
  15.   if (Value > 999999999999) then
  16.     Insert(TS, Result, Length(Result)-11-3*TSLen);
  17.   if (Value > 999999999999999) then
  18.     Insert(TS, Result, Length(Result)-14-4*TSLen);
  19.   if (Value > 999999999999999999) then
  20.     Insert(TS, Result, Length(Result)-17-5*TSLen);
  21. end;
  22.  
  23. function IntToStr(Value: Int64; const TS: String): String; overload;
  24. var
  25.   TSLen: Integer;
  26. begin
  27.   Str(Value, Result);
  28.   if (TS = '') then
  29.     Exit;
  30.   TSLen := Length(TS);
  31.   if (Value > 999) or (Value < -999) then
  32.     Insert(TS, Result, Length(Result)-2);
  33.   if (Value > 999999) or (Value < -999999)then
  34.     Insert(TS, Result, Length(Result)-5-TSLen);
  35.   if (Value > 999999999) or (Value < -999999999) then
  36.     Insert(TS, Result, Length(Result)-8-2*TSLen);
  37.   if (Value > 999999999999) or (Value < -999999999999) then
  38.     Insert(TS, Result, Length(Result)-11-3*TSLen);
  39.   if (Value > 999999999999999) or (Value < -999999999999999) then
  40.     Insert(TS, Result, Length(Result)-14-4*TSLen);
  41.   if (Value > 999999999999999999) or (Value < -999999999999999999) then
  42.     Insert(TS, Result, Length(Result)-17-5*TSLen);
  43. end;

And this is slightly faster:
Code: Pascal  [Select][+][-]
  1. //only works if Length(AThousandSep) <= 4, which should allow for any UTF8 codepoint
  2. function InsertThousandSep(const ValueS, AThousandSep: String): String;
  3. const
  4.   MaxLen = Length('18    446    744    073    709    551    615');   //MaxUInt64
  5. var
  6.   ResPos, SLen, i, j, SpaceCount: Integer;
  7. begin
  8.   Result := StringOfChar(#32,MaxLen);
  9.   SLen := Length(ValueS);
  10.   ResPos := MaxLen;
  11.   for i := Length(ValueS) downto 1 do
  12.   begin
  13.     if ((SLen-i) in [3,6,9,12,15,18]) {and (i <> 1)} then
  14.     begin
  15.       for j := Length(AThousandSep) downto 1 do
  16.       begin
  17.         Result[ResPos] := AThousandSep[j];
  18.         Dec(ResPos);
  19.       end;
  20.     end;
  21.     Result[ResPos] := ValueS[i];
  22.     Dec(ResPos);
  23.   end;
  24.   SpaceCount := 0;
  25.   i := 1;
  26.   while (Result[i] = #32) do
  27.   begin
  28.     Inc(SpaceCount);
  29.     Inc(i);
  30.   end;
  31.   if (SpaceCount > 0) then
  32.     System.Delete(Result, 1, SpaceCount);
  33. end;
  34.  
  35. function IntToStr(Value: UInt64; const TS: String): String; overload;
  36. var
  37.   S: String;
  38. begin
  39.   if (TS = '') then
  40.     Exit(IntToStr(Value));
  41.   Str(Value, S);
  42.   Result := InsertThousandSep(S, TS);
  43. end;
  44.  
  45. function IntToStr(Value: Int64; const TS: String): String; overload;
  46. var
  47.   S: String;
  48. begin
  49.   if (TS = '') then
  50.     Exit(IntToStr(Value));
  51.   Str(Value, S);
  52.   Result := InsertThousandSep(S, TS);
  53. end;

Bart

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: IntToStr with national convention?
« Reply #11 on: August 02, 2020, 11:25:35 pm »
Hi!

This procedure inserts a blockchar every blocklen counted from the end.
Use a  blocklen of 2 to show hexadecimals and 3 to show decimals.
blockchar should be your national separator


Code: Pascal  [Select][+][-]
  1. function goodLooking (s: string; blocklen: integer; blockchar : char) : string;
  2. var i,k : integer;
  3.      begin
  4.        result := ''; k := 1;
  5.        for i := length(s) downto 1 do
  6.        begin
  7.                 result := s[i]+result;
  8.                 if (k mod Blocklen = 0) and (i>1) then result := blockchar+ result;
  9.                 inc (k);
  10.         end;
  11.      end;
  12.  

Winni

Bart

  • Hero Member
  • *****
  • Posts: 5290
    • Bart en Mariska's Webstek
Re: IntToStr with national convention?
« Reply #12 on: August 02, 2020, 11:49:49 pm »
Appr. 3.8 times slower (and only caters for chars)  O:-)
(I guess repeated insert() kills the speed, tested with High(UInt64) as input, so probably worst case scenario.)

Bart
« Last Edit: August 02, 2020, 11:52:20 pm by Bart »

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: IntToStr with national convention?
« Reply #13 on: August 02, 2020, 11:55:18 pm »
Hi Bart!

I knew it before that this is to simple for you .....

And it should only be used for digits or hexdigits:
all ASCII.

Or do you print your numbers in UTF8-Cherokee?

Winni


winni

  • Hero Member
  • *****
  • Posts: 3197
Re: IntToStr with national convention?
« Reply #14 on: August 03, 2020, 12:13:05 am »
Awful slow.

0.000569  Milliseconds. Awful slow.
You really recognize it.

For those who live in a world with seconds:
0.000000569 seconds

Winni



 

TinyPortal © 2005-2018