removing leading zeros from delphi string - delphi

Delphi 7
How do i remove leading zeros in a delphi string?
Example:
00000004357816
function removeLeadingZeros(ValueStr: String): String
begin
result:=
end;

Code that removes leading zeroes from '000'-like strings correctly:
function TrimLeadingZeros(const S: string): string;
var
I, L: Integer;
begin
L:= Length(S);
I:= 1;
while (I < L) and (S[I] = '0') do Inc(I);
Result:= Copy(S, I);
end;

function removeLeadingZeros(const Value: string): string;
var
i: Integer;
begin
for i := 1 to Length(Value) do
if Value[i]<>'0' then
begin
Result := Copy(Value, i, MaxInt);
exit;
end;
Result := '';
end;
Depending on the exact requirements you may wish to trim whitespace. I have not done that here since it was not mentioned in the question.
Update
I fixed the bug that Serg identified in the original version of this answer.

Use JEDI Code Library to do this:
uses JclStrings;
var
S: string;
begin
S := StrTrimCharLeft('00000004357816', '0');
end.

Probably not the fastest one, but it's a one-liner ;-)
function RemoveLeadingZeros(const aValue: String): String;
begin
Result := IntToStr(StrToIntDef(aValue,0));
end;
Only works for numbers within the Integer range, of course.

Searching for this in 2021 there is a much better solution that I found now and that is using the TStringHelper function TrimLeft as follows
myString := myString.TrimLeft(['0']);
see here for more on the documentation http://docwiki.embarcadero.com/Libraries/Sydney/en/System.SysUtils.TStringHelper.TrimLeft

Try this:
function TFrmMain.removeLeadingZeros(const yyy: string): string;
var
xxx : string;
begin
xxx:=yyy;
while LeftStr(xxx,1) = '0' do
begin
Delete(xxx,1,1);
end;
Result:=xxx;
end;

Related

Read mutiple values from ini file into TCombobox

I have an ini file which contains the following:
[Colours]
1 = Red
2 = Blue
3 = Green
4 = Yellow
In my app I have a TComboBox which I would like to populate with the colours in the ini file.
Does anyone know how I'd go about this?
Thanks,
You can get a list of names in a section by using TIniFile.ReadSection() and then iterate to get the values:
procedure TForm1.LoadFile(const AFilename: String);
var
I: TIniFile;
L: TStringList;
X: Integer;
N: String;
V: String;
begin
I:= TIniFile.Create(AFilename);
try
L:= TStringList.Create;
try
ComboBox1.Items.Clear;
I.ReadSection('Colours', L);
for X := 0 to L.Count-1 do begin
N:= L[X]; //The Name
V:= I.ReadString('Colours', N, ''); //The Value
ComboBox1.Items.Add(V);
end;
finally
L.Free;
end;
finally
I.Free;
end;
end;
As an alternative, you could also dump the name/value pairs within the section into a single TStringList and read each value using the string list's built-in capabilities...
procedure TForm1.LoadFile(const AFilename: String);
var
I: TIniFile;
L: TStringList;
X: Integer;
N: String;
V: String;
begin
I:= TIniFile.Create(AFilename);
try
L:= TStringList.Create;
try
ComboBox1.Items.Clear;
I.ReadSectionValues('Colours', L);
for X := 0 to L.Count-1 do begin
N:= L.Names[X]; //The Name
V:= L.Values[N]; //The Value
ComboBox1.Items.Add(V);
end;
finally
L.Free;
end;
finally
I.Free;
end;
end;
On a side-note, Ini files do not have spaces on either side of the = sign, unless of course you want that space as part of the actual name or value.
try this, without reading the file twice:
uses IniFiles;
procedure TForm1.Button1Click(Sender: TObject);
var
lIni : TIniFile;
i: Integer;
begin
lIni := TIniFile.Create('c:\MyFile.ini');
try
lIni.ReadSectionValues('Colours', ComboBox1.Items);
for i := 0 to ComboBox1.Items.Count - 1 do
ComboBox1.Items[i] := ComboBox1.Items.ValueFromIndex[i];
finally
FreeAndNil(lIni);
end;
end;

Read reg_binary as string using Tregistry Class in delphi

I am trying to get the reg_binary as string from a registry key.
This is my function
function ReadBinString(key: string; AttrName: string): string;
var
ReadStr: TRegistry;
begin
// Result := '';
ReadStr := TRegistry.Create(KEY_WRITE OR KEY_WOW64_64KEY);
ReadStr.RootKey := HKEY_LOCAL_MACHINE;
if ReadStr.OpenKey(key, true) then
begin
Result := ReadStr.GetDataAsString(AttrName);
end;
ReadStr.CloseKey;
ReadStr.Free;
end;
and here is my registry key Export :
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\ZES\ACINFO]
"iamthere"=dword:00000001
"ArrayOrder"=hex:4d,79,45,78,63,6c,75,64,65
the problem is , the function returns empty string
I even tried running as administrator to make sure that it is not permissions.
Any help ?
Expanding on my comment to the question, I'd use code like so:
function ReadBinString(RootKey: HKEY; Access: LongWord; const KeyName,
ValueName: string; Encoding: TEncoding): string;
var
Registry: TRegistry;
Bytes: TBytes;
begin
Registry := TRegistry.Create(Access);
try
Registry.RootKey := RootKey;
if Registry.OpenKeyReadOnly(KeyName) then begin
SetLength(Bytes, Registry.GetDataSize(ValueName));
Registry.ReadBinaryData(ValueName, Pointer(Bytes)^, Length(Bytes));
Result := Encoding.GetString(Bytes);
end else begin
Result := '';
end;
finally
Registry.Free;
end;
end;
For your data you would call it like so:
Value := ReadBinString(HKEY_LOCAL_MACHINE, KEY_WOW64_64KEY, 'Software\ZES\ACINFO',
'ArrayOrder', TEncoding.ANSI);
Notes:
I have avoided hard-coding the root key.
I have used TEncoding to decode the byte array to text. This is far more effective than GetDataAsString.
I have allowed the caller to specify the encoding to be used.
I have allowed the caller to specify the access flags.
I have used OpenKeyReadOnly because we do not require write access.
Thanks to David Heffernan I came with this solution:
function ReadBinString(key: string; AttrName: string): string;
var
ReadStr: TRegistry;
hexStr : string;
I : Integer;
begin
// Result := '';
ReadStr := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
ReadStr.RootKey := HKEY_LOCAL_MACHINE;
if ReadStr.OpenKey(key, true) then
begin
hexStr := ReadStr.GetDataAsString(AttrName);
hexStr := hexStr.Replace(',','');
for I := 1 to length (hexStr) div 2 do
Result:= Result+Char(StrToInt('$'+Copy(hexStr,(I-1)*2+1,2)));
end;
ReadStr.CloseKey;
ReadStr.Free;
end;
Thanks to David Heffernan again ... this worked for me :
function ReadBinString(key: string; AttrName: string): string;
var
ReadStr: TRegistry;
hexStr: string;
I: Integer;
Bytes: TBytes;
Encoding: TEncoding;
begin
Encoding := TEncoding.ANSI;
Result := '';
ReadStr := TRegistry.Create(KEY_READ OR KEY_WOW64_64KEY);
ReadStr.RootKey := HKEY_LOCAL_MACHINE;
try
if ReadStr.OpenKeyReadOnly(key ) then
begin
SetLength(Bytes, ReadStr.GetDataSize(AttrName));
ReadStr.ReadBinaryData(AttrName, Pointer(Bytes)^, Length(Bytes));
Result := Encoding.GetString(Bytes);
// hexStr := ReadStr.GetDataAsString(AttrName);
//
// hexStr := hexStr.Replace(',','');
// for I := 1 to length (hexStr) div 2 do
// Result:= Result+Char(StrToInt('$'+Copy(hexStr,(I-1)*2+1,2)));
end;
except
end;
ReadStr.CloseKey;
ReadStr.Free;
end;

How to encode strings with EncdDec library

I have this basic code that should encode a string and then get it back. However, the decoded text is garbage.
procedure TForm5.Button2Click(Sender: TObject);
VAR s1, s2, s3: String;
i: Integer;
begin
for i:= 1 to 200
DO s1:= s1+ char(Random(255));
s1:= EncdDecd.EncodeString(s1);
s3:= EncdDecd.DecodeString(s2);
if s1= s3
then Caption:= 'Equal'
else Caption:= 'Not equal';
end;
Update:
It works if I do char(Random(128)) instead of 255!
The problem is here, in the Soap.EncdDecd unit:
function EncodeString(const Input: string): string;
var
InStr, OutStr: TStringStream;
begin
InStr := TStringStream.Create(Input);
try
OutStr := TStringStream.Create('');
try
EncodeStream(InStr, OutStr);
Result := OutStr.DataString;
finally
OutStr.Free;
end;
finally
InStr.Free;
end;
end;
This code has not been updated for Unicode. The use of TStringStream for InStr without specifying an encoding is the problem. With no encoding specified, ANSI is used to decode to a byte array. And so only characters in the local ANSI code page are decoded correctly.
The same mistake is made in the other direction in DecodeString.
You can fix this readily enough by creating variants that specify a full Unicode encoding. For example:
function EncodeString(const Input: string): string;
var
InStr, OutStr: TStringStream;
begin
InStr := TStringStream.Create(Input, TEncoding.UTF8);
try
OutStr := TStringStream.Create('');
try
EncodeStream(InStr, OutStr);
Result := OutStr.DataString;
finally
OutStr.Free;
end;
finally
InStr.Free;
end;
end;
function DecodeString(const Input: string): string;
var
InStr, OutStr: TStringStream;
begin
InStr := TStringStream.Create(Input);
try
OutStr := TStringStream.Create('', TEncoding.UTF8);
try
DecodeStream(InStr, OutStr);
Result := OutStr.DataString;
finally
OutStr.Free;
end;
finally
InStr.Free;
end;
end;
It doesn't matter which encoding you specify so long as it is a full Unicode encoding, and you use the same encoding in both directions!
Clearly Embarcadero should be encouraged to make this change to their code.
This program demonstrates that the fix works:
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Classes, Soap.EncdDecd;
function EncodeString(const Input: string): string;
var
InStr, OutStr: TStringStream;
begin
InStr := TStringStream.Create(Input, TEncoding.UTF8);
try
OutStr := TStringStream.Create('');
try
EncodeStream(InStr, OutStr);
Result := OutStr.DataString;
finally
OutStr.Free;
end;
finally
InStr.Free;
end;
end;
function DecodeString(const Input: string): string;
var
InStr, OutStr: TStringStream;
begin
InStr := TStringStream.Create(Input);
try
OutStr := TStringStream.Create('', TEncoding.UTF8);
try
DecodeStream(InStr, OutStr);
Result := OutStr.DataString;
finally
OutStr.Free;
end;
finally
InStr.Free;
end;
end;
const
N = 256;
var
i: Integer;
s1, s2: string;
begin
SetLength(s1, N);
for i := 1 to N do
s1[i] := Chr(i-1);
s2 := Soap.EncdDecd.EncodeString(s1);
s2 := Soap.EncdDecd.DecodeString(s2);
Writeln(s1=s2);
s2 := EncodeString(s1);
s2 := DecodeString(s2);
Writeln(s1=s2);
end.
Output:
FALSE
TRUE
Quick fix:
ANSI is enough for me since the text resulted after encoding must be as short as possible (easy to send via email). So, I put my ANSI string into a stream and encoded/decoded the stream. It worked!
Jesus. Some documentation (at least a single comment line) would have been nice!

Counting folders within a folder

Does anyone know a code I can use to count the number of folders in a specified directory?
The very simplest code that I know of uses TDirectory from the IOUtils unit:
function GetDirectoryCount(const DirName: string): Integer;
begin
Result := Length(TDirectory.GetDirectories(DirName));
end;
TDirectory.GetDirectories actually returns a dynamic array containing the names of the directories so this is somewhat inefficient. If you want the most efficient solution then you should use FindFirst to enumerate.
function GetDirectoryCount(const DirName: string): Integer;
var
res: Integer;
SearchRec: TSearchRec;
Name: string;
begin
Result := 0;
res := FindFirst(TPath.Combine(DirName, '*'), faAnyFile, SearchRec);
if res=0 then begin
try
while res=0 do begin
if SearchRec.FindData.dwFileAttributes and faDirectory<>0 then begin
Name := SearchRec.FindData.cFileName;
if (Name<>'.') and (Name<>'..') then begin
inc(Result);
end;
end;
res := FindNext(SearchRec);
end;
finally
FindClose(SearchRec);
end;
end;
end;

Converting Strings to Hex in a performant way

I developed the following function to convert strings to hex values.
function StrToHex(const S: String): String;
const
HexDigits: array[0..15] of Char = '0123456789ABCDEF';
var
I: Integer;
P1: PChar;
P2: PChar;
B: Byte;
begin
SetLength(Result, Length(S) * 2);
P1 := #S[1];
P2 := #Result[1];
for I := 1 to Length(S) do
begin
B := Byte(P1^);
P2^ := HexDigits[B shr 4];
Inc(P2);
P2^ := HexDigits[B and $F];
Inc(P1);
Inc(P2);
end;
end;
Now I was wondering whether there is a more efficient way to convert the strings?
Depending on your Delphi version:
D5-D2007
uses classes;
function String2Hex(const Buffer: Ansistring): string;
begin
SetLength(result, 2*Length(Buffer));
BinToHex(#Buffer[1], #result[1], Length(Buffer));
end;
D2009+
uses classes;
function String2Hex(const Buffer: Ansistring): string;
begin
SetLength(result, 2*Length(Buffer));
BinToHex(#Buffer[1], PWideChar(#result[1]), Length(Buffer));
end;
Try this one
function String2Hex(const Buffer: Ansistring): string;
var
n: Integer;
begin
Result := '';
for n := 1 to Length(Buffer) do
Result := LowerCase(Result + IntToHex(Ord(Buffer[n]), 2));
end;
I know this is a very old topic, but I feel like I kinda need to share my code regarding the question. For years I use my own HexEncode, very similar with Forlan's code up there, but just today I found a faster way to encode Hex. With my old HexEncode, encoding a 180kb binary file took about 50 seconds, while with this function it took up 6 seconds.
function getHexEncode(txt : AnsiString) : AnsiString;
var
a : integer ;
st : TStringStream;
buf : array [0..1] of AnsiChar;
tmp : ShortString;
begin
st := TStringStream.Create;
st.Size := Length(txt)*2;
st.Position := 0;
for a:=1 to Length(txt) do
begin
tmp := IntToHex(Ord(txt[a]),2);
buf[0] := tmp[1];
buf[1] := tmp[2];
st.Write(buf,2);
end;
st.Position := 0;
Result := st.DataString;
st.Free;
//Result := ''; //my old code
//for a:=1 to Length(txt) do Result := Result+IntToHex(Ord(txt[a]),2); //my old code
end;
It seems good enough, you could always have a byte->2 hex digits lookup table, but that (and similar optimizations) seems like overkill to me in most cases.
// StrToInt('$' + MyString);
Oops, did not read the question very good...

Resources