thousand separator in integer values [duplicate] - delphi

var i : integer;
i := 1234567;
Given the above, I want the string "1,234,567" as output (assuming UK locale). IntToStr just gives me "1234567". I'm sure there's a one-liner for this, but I can't find it...

Try the format function.
Label1.Caption := Format('%.0n', [i + 0.0]);

Or if you need to be threadsafe or want to ensure you use the system default locale or want to specify one:
function FormatIntFromLCID(const AValue: Integer; const LCID: Integer = LOCALE_SYSTEM_DEFAULT): string;
var
AFormatSettings: TFormatSettings;
begin
GetLocaleFormatSettings(LCID, AFormatSettings);
Result := FormatFloat('#,##0',AValue, AFormatSettings);
end;
see this post for a more complete discussion about formatting/locales

s := FormatFloat('#,##0', i);

I have this function to do it, where d means perhaps decimal number:
function dn(i: integer): string;
begin
result := format('%.0n', [i.ToDouble])
end;

Format('%n', [12345.678]);

stringreplace(format('%n',[1234567.0]),'.00','',[]);

Related

How to hash using DCPcrypt?

I have scowered the net trying to find an example of a function, how to hash text with Sha1 and DCPcrypt.
I have the below example. Seems to pop up the whole time.
But it returns chinese characters every time. Please assist in corecting the function.
function TForm1.EncryptThis(aString : string) : string;
var
Cipher: TDCP_cast256;
KeyStr: string;
begin
KeyStr:= '';
Cipher:= TDCP_cast256.Create(Self);
Cipher.InitStr(KeyStr,TDCP_sha1);
result := Cipher.EncryptString(aString);
Cipher.Burn;
Cipher.Free;
end;
UPDATE:
Using the links and info belowe, I built these functions. But as I said, This does not make alot of sense to me. So please excuse the ignorance.
THe code however does not work. Its output is: 3F3F3F3F3F3F3F3F3F3F00000000000000000000 whereas it should be 40bd001563085fc35165329ea1ff5c5ecbdbbeef since i told the program to has 123.
Please help.
function CalcDigest(text: string): string;
var
x: TDCP_hash;
begin
x := TDCP_sha1.Create(nil);
try
x.Init;
x.UpdateStr(text);
SetLength(Result, x.GetHashSize div 8);
x.Final(Result[1]);
finally
x.Free;
end;
end;
function String2Hex(const Buffer: Ansistring): string;
begin
SetLength(result, 2*Length(Buffer));
BinToHex(#Buffer[1], PWideChar(#result[1]), Length(Buffer));
end;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
memo2.Lines.Add(String2Hex(CalcDigest(memo1.Lines.Strings[0])));
end;
Judging by this, you can do it this way:
function CalcDigest(text: string): string;
var
x: TDCP_hash;
begin
x := TDCP_sha1.Create(nil);
try
x.Init;
x.UpdateStr(text);
SetLength(Result, x.GetHashSize div 8);
x.Final(Result[1]);
finally
x.Free;
end;
end;
You may want to encode the hash before printing, because the output is binary. See for example this question.
I am not very familiar with DCPCrypt. You can also use other libraries.
1) Indy - usually included in Delphi
function SHA1Text(const s: string): string;
begin
with TIdHashSHA1.Create do
try
Result:=LowerCase(HashStringAsHex(s));
finally
Free;
end;
end;
2) Wolfgang Ehrhardt's libraries (fastest as far as I know) from
http://www.wolfgang-ehrhardt.de/crchash_en.html
function SHA1Text(const s: string): string;
var
Context: THashContext;
SHA1Digest: TSHA1Digest;
begin
SHA1Init(Context);
SHA1Update(Context, pChar(s), length(s));
SHA1Final(Context, SHA1Digest);
Result:=HexStr(#SHA1Digest, SizeOf(SHA1Digest));
end;
NOTE: it is from Delphi 7. You will need to update it if you use unicode Delphi.

FindFirst, FindNext (Delphi Xe, Win7) rank is not correct

I have some files in a directory. I try get these files with FindFirst and FindNext but I can't get same order on Windows 7.
C:\Test
SampleFile.0.png
SampleFile.1.png
SampleFile.2.png
SampleFile.3.png
SampleFile.4.png
SampleFile.5.png
SampleFile.6.png
SampleFile.7.png
SampleFile.8.png
SampleFile.9.png
SampleFile.10.png
SampleFile.11.png
SampleFile.12.png
SampleFile.13.png
SampleFile.14.png
SampleFile.15.png
SampleFile.16.png
SampleFile.17.png
SampleFile.18.png
SampleFile.19.png
SampleFile.20.png
SampleFile.21.png
SampleFile.22.png
When I try using my code I've got
SampleFile.0.png
SampleFile.1.png
SampleFile.10.png
SampleFile.11.png
SampleFile.12.png
SampleFile.13.png
SampleFile.14.png
SampleFile.15.png
SampleFile.16.png
SampleFile.17.png
SampleFile.18.png
SampleFile.19.png
SampleFile.2.png
SampleFile.20.png
SampleFile.21.png
.
.
.
How can I get file list on correct rank order?
Procedure Test;
var
sr : TSearchRec;
i : integer;
ListFiles : TStringList;
begin
ListFiles := TStringList.Create;
i := FindFirst('c:\test\*.png', faDirectory, sr);
while i = 0 do begin
ListFiles.Add(ExtractFileName(sr.FindData.cFileName));
i := FindNext(sr);
end;
FindClose(sr);
end;
Note : Result is still wrong, if I can use ListFiles.Sorted = True
I think I've a solution, created a function.
function SortFilesByName(List: TStringList; Index1, Index2: Integer): integer;
var
FileName1, FileName2: String;
i, FileNumber1, FileNumber2: Integer;
begin
FileName1 := ChangeFileExt(ExtractFileName(List[Index1]), '');
FileName2 := ChangeFileExt(ExtractFileName(List[Index2]), '');
i := POS('.', FileName1)+1;
FileNumber1 := StrToInt(Copy(FileName1, i, MaxInt));
i := POS('.', FileName2)+1;
FileNumber2 := StrToInt(Copy(FileName2, i, MaxInt));
Result := (FileNumber1 - FileNumber2);
end;
I've added another line
ListFiles.CustomSort(SortFilesByName); //(ListFiles,1,2):integer);
before
FindClose(sr);
As jachguate said, the sorting is done by Explorer.exe, not the filesystem. FindFirst/FindNext does not guarantee any specific sorting, including plain ASCII based, so you shouldn't rely on it. You don't, however, need to re-implement the numeric sort in Delphi. Windows exposes the one it uses as StrCmpLogicalW, which is in shlwapi.dll. The import looks like this:
function StrCmpLogicalW(psz1, psz2: PWideChar): Integer; stdcall;
external 'shlwapi.dll'
It is possible to disable that behavior in Windows. If you want to follow the order that Windows uses, you need to call SHRestricted with the REST_NOSTRCMPLOGICAL value. If it returns true you should use AnsiCompareStr instead.
const
// Use default CompareString instead of StrCmpLogical
REST_NOSTRCMPLOGICAL = $4000007E;
function SHRestricted(rest: DWORD): LongBool; stdcall; external 'shell32.dll';
So your final sort function should be something like this:
function CompareFilenames(const AFilename1, AFilename2: string): Integer;
begin
if SHRestricted(REST_NOSTRCMPLOGICAL) then
Result := AnsiCompareStr(AFilename1, AFilename2)
else
Result := StrCmpLogicalW(PWideChar(AFilename1), PWideChar(AFilename2));
end;
You can cache the result of the SHRestricted call, but if you do you need to watch for the WM_SETTINGSCHANGE broadcast message and re-read it when you get one.
The different orders you see in the windows explorer is implemented in explorer.exe and not in the file system.
The Numerical sort order is a new feature in windows 7, so if you sort by name and you have a bunch of files with a prefix followed by numbers, the explorer "identifies" that pattern and doesn't present a list sorted by name in the traditional way, but sorted by prefix and then by number (as if the string were a Integer number).
If you want to do the same in Delphi, you can do it by adding all the file names returned by FindFirst/FindNext to a TSlist and then sort the string list using this compare function:
var
FileNames: TList<string>;
begin
FileNames := TList<string>.Create;
try
SearchForFiles(FileNames); //here you add all the file names
//sort file names a la windows 7 explorer
FileNames.Sort(System.Generics.Defaults.TComparer<string>.Construct(
function (const s1, s2: string): Integer
procedure ProcessPrefix(const fn: string; var prefix, number: string);
var
I: Integer;
begin
for I := length(fn) downto 1 do
if not TCharacter.IsDigit(fn[I]) then
begin
Prefix := Copy(fn, 1, I);
number := Copy(fn, I+1, MaxInt);
Break;
end;
end;
var
prefix1, prefix2: string;
number1, number2: string;
fn1, fn2: string;
begin
//compare filenames a la windows 7 explorer
fn1 := TPath.GetFileNameWithoutExtension(s1);
fn2 := TPath.GetFileNameWithoutExtension(s2);
ProcessPrefix(fn1, prefix1, number1);
ProcessPrefix(fn2, prefix2, number2);
if (Number1 <> '') and (Number2 <> '') then
begin
Result := CompareText(prefix1, prefix2);
if Result = 0 then
Result := CompareValue(StrToInt(number1), StrToInt(Number2));
end
else
Result := CompareText(s1, s2);
end
));
UseYourSortedFileNames(FileNames);
finally
FileNames.Free;
end;
end;
By "rank", you mean sort order.
The files are sorting in the proper order (based on the ASCII value of the characters). 2 comes after 19 because the comparison is only made up to the same number of characters in both names, and '2' comes after 1.
If you want them to sort properly as numbers, you need to left-pad the numbers with zeros so they're all the same width (eg., instead of SampleFile.2.png, use SampleFile.02.png). This will cause '02' to come before 19 so they sort correctly numerically.
You can fix the numbering issue by using something like:
PngFileName := Format('SampleFile.%.2d.png', [Counter]);

Overload TFormatSettings and incompatible types

i have this procedure:
procedure Initialize(out FormatSettings: TFormatSettings);
const
LongDayNamesEx : array [1..7] of string = ('Lunedì', 'Martedì', 'Mercoledì', 'Giovedì', 'Venerdì', 'Sabato', 'Domenica');
LongMonthNamesEx : array [1..12] of string = ('Gennaio', 'Febbraio', 'Marzo', 'Aprile', 'Maggio', 'Giugno', 'Luglio', 'Agosto', 'Settembre', 'Ottobre', 'Novembre', 'Dicembre');
begin
FormatSettings := TFormatSettings.Create;
with FormatSettings do
begin
LongDayNames := LongDayNamesEx;
LongMonthNames := LongMonthNamesEx;
end;
end;
And i get an error about incompatible types (E2008). How i can solve this problem? I don't want to use something as:
LongDayNames[1] := 'Lunedì';
LongDayNames[2] := 'Martedì';
...
LongDayNames[7] := 'Domenica';
LongMonthNames[1] := 'Gennaio';
LongMonthNames[2] := 'Febbraio';
...
LongMonthNames[12] := 'Dicembre';
if not stricly necessary.
Thanks for help.
You can do like this:
type
TDayNameArray = array[1..7] of string;
const
LongDayNamesEx: TDayNameArray = ('Måndag', 'Tisdag', 'Onsdag', 'Torsdag',
'Fredag', 'Lördag', 'Söndag');
var
fs: TFormatSettings;
begin
TDayNameArray(fs.LongDayNames) := LongDayNamesEx;
Andreas gave you a good answer to the direct question that you asked.
Taking a different approach, I think you could solve your problem more easily by passing the locale when you initlialise the object. For example:
FormatSettings := TFormatSettings.Create('it-IT');
for Italian. Then the system will fill out the locale specific settings, day names, month names etc.
Or perhaps you would use the overload that takes a locale ID is more appropriate. No matter, you surely get the idea.
To answer the question you asked directly, the obvious solution is to use a for loop. Combine a record helper and open array parameters to make it more easily called:
type
TTFormatSettingsHelper = record helper for TFormatSettings
procedure SetLongDayNames(const Values: array of string);
end;
procedure TTFormatSettingsHelper.SetLongDayNames(const Values: array of string);
var
Index: Integer;
Value: string;
begin
Assert(high(Values)-low(Values)
= high(Self.LongDayNames)-low(Self.LongDayNames));
Index := low(Self.LongDayNames);
for Value in Values do
begin
Self.LongDayNames[Index] := Value;
inc(Index);
end;
end;
And then to call this you simply write:
FormatSettings.SetLongDayNames(['Lunedì', 'Martedì', 'Mercoledì', 'Giovedì',
'Venerdì', 'Sabato', 'Domenica']);

Syntax for local variable absolute to another variable with some offset

Is there a way that I can declare a variable with an absolute address that has some offset to the variable that it refers to. For instance, instead of:
function RefCount(const s: string): Integer;
begin
Result := PInteger(Integer(s) - 8)^;
end;
is there some way that I can do:
function RefCount(const s: string): Integer;
var
Count: PInteger absolute s {- 8 ?} ;
begin
Result := Count^;
end;
(The example is to illustrate only, it is not necessarily useful..)
No, I don't think there is an 'extended syntax' of the absolute keyword. The documentation is here, and, as far as I know, there are no undocumented features related to this keyword.
There is no syntax for what you ask.
What you can do, however, is use pointer arithmetic (if you are using a version that supports it), eg:
function RefCount(const s: string): Integer;
begin
if s <> '' then
Result := (PInteger(s) - 2)^;
else
Result := 0;
end;
A more reliably approach is to use the StrRec record type instead, which is what a String actually contains internally:
function RefCount(const s: string): Integer;
begin
if s <> '' then
Result := (PStrRec(s) - 1)^.refCnt
else
Result := 0;
end;
Or, the non pointer arithmetic version:
function RefCount(const s: string): Integer;
begin
if s <> '' then
Result := PStrRec(LongInt(s) - SizeOf(StrRec))^.refCnt
else
Result := 0;
end;
BTW, starting with D2009+, the System unit has its own StringRefCount() function that retreive a String's reference count.

Inverse function of FormatDateTime

I am looking for a function to reverse any string (YYYYMDD,YY/MM/DD,YYMMDD,...) created by the function FormatDateTime to datetime.
example
I have a string-date in format YYYYMMDDcreated by FormatDateTime
mydatestr:=FormatDateTime('YYYYMMDD',Mydate);
now how I can convert mydatestr to DateTime again?
UPDATE
these functions
function StrToDate(const S: string): TDateTime; overload;
function StrToDate(const S: string;
const FormatSettings: TFormatSettings): TDateTime; overload;
function StrToDateTime(const S: string): TDateTime; overload;
function StrToDateTime(const S: string;
const FormatSettings: TFormatSettings): TDateTime; overload;
do not support passing a string with the format to convert.
I am looking something like this
Mydatetime:=InvFormatDatetime('20091225','yyyymmdd');
or
Mydatetime:=InvFormatDatetime('20090108','yyyyddmm');
It is quite easy with existing solution, StrToDateFmt function in rxDateutil.pas unit from RX package, which can be downloaded here: http://sourceforge.net/projects/rxlib/
EDIT:
Mentioned above function and StrToDateFmt from rxDateutil.pas are doing exactly what you expect, converting string to datetime using specified string mask, the code is too large to be included as this unit contains also other date functions, some of them required for converting string to date.
Example of use:
Result := StrToDateFmtDef('MM/DD/YYYY', '11/11/2011', Now);
Note: Unfortunately, as pointed out by Martien in this answer's comments, this solution will not work for cases where the date includes no date separator. However, I'm going to leave the answer up for anyone that may find it useful regardless.
I liked Tuncay's answer but there were a couple of problems with it. I'd have left a comment but I don't have enough reputation points.
So, here's the corrected version of Tuncay's answer (amending "TFormatSetting" missing an "s" and specified the format settings date separator):
function AnyStringToDate(fmt, dt: String) : TDateTime;
var
fs : TFormatSettings;
Begin
fs := TFormatSettings.Create;
fs.DateSeparator := '-';
fs.ShortDateFormat := fmt;
result := StrToDateDef(dt, 0, fs);
End;
Identical to Tuncay's answer, AnyStringToDate can be used as follows:
mydate := AnyStringToDate('YYYY-MM-DD', '2015-01-20');
Without using any external library, you can do something like:
function AnyStringToDate(fmt, dt: String) : TDateTime;
var
fs : TFormatSettings;
Begin
fs := TFormatSettings.Create;
fs.ShortDateFormat := fmt;
result := StrToDateDef(dt, 0, fs);
End;
and then use it like:
mydate := AnyStringToDate('YYYY-MM-DD', '2015-01-20');
I havent compiled this, but the idea simple.
You could use StrToDateFmt function of JvJCLUtils unit belonging to JEDI Code Library
in your case :
Function InvFormatDatetime ( dateString :String; dateFormat :String ) : TDateTime;
begin
Result := JvJCLUtils.StrToDateFmt ( dateFormat, dateString );
end;
Did you check StrToDate and StrToDateTime ?
I know its too late but just for interest sake, with Delphi XE6 onwards you can now do the following
uses
System.DateUtils;
var
dt: TDateTime;
begin
dt := ISO8601ToDate('20190408');
end.
dt will now be 2019/04/08
Function InvFormatDatetime (Cadena:String; Formato:String) : TDateTime;
Var
PosD, PosM, PosY : Integer;
sD, sM, sY : String;
begin
sd := '0';
sm := '0';
sy := '0';
If Length(Cadena) = Length(Formato) Then
Begin
Formato := UpperCase(Formato);
PosD := Pos('D',Formato);
PosM := Pos('M',Formato);
PosY := Pos('Y',Formato);
sd := Copy(Cadena,PosD,2);
sm := Copy(Cadena,PosM,2);
if Length(Cadena) = 6 then
begin
sy := Copy(Cadena,PosY,2);
if StrToInt(sy) > 50 then
sy := '19'+sy
else
sy := '20'+sy;
end
else
sy := Copy(Cadena,Posy,4);
End;
Result := EncodeDate(StrToInt(sY),
StrToInt(sM),
StrToInt(sD));
End;
greetings

Resources