Reverse FloatToStrF Result - delphi

Is there a function to reverse FloatToStrF? By that I mean go back from currency money format to string format. For example:
Edit1.Text := FloatToStrF(10000, ffCurrency, 15, 4);
The result
Edit1.Text = '$10,000.0000'
I'm wondering if there is somthing like StrToFloatF so
Edit1.Text = '10000';
thank's

How about:
function RemoveAnythingButNumbers(aString: string): string;
var
C: Char;
begin
Result := '';
for C in aString do
begin
if C in ['0'..'9'] then Result := Result + C;
end;
end;
Note that I corrected the set of numbers.

Related

How to get the string representation of a ShortCut Key including the SHIFTSTATE?

In a Delphi 10.4.2 Win32 VCL Application, and based on the question + solution here which provides a way to get the string representation of a Shortcut Key (but presumably with no possibility to also pass a SHIFTSTATE for the Shortcut Key) I wrote this code:
function MyGetSpecialShortcutName(ShortCut: TShortCut): string;
// gets shortcut name for e.g. VK_NUMPAD0 where TMenuItem.Shortcut gets the wrong shortcut name
var
ScanCode: Integer;
KeyName: array[0..255] of Char;
begin
Result := '';
FillChar(KeyName, SizeOf(KeyName), 0);
ScanCode := Winapi.Windows.MapVirtualKey(LoByte(Word(ShortCut)), 0) shl 16;
if ScanCode <> 0 then
begin
if Winapi.Windows.GetKeyNameText(ScanCode, KeyName, Length(KeyName)) <> 0 then
Result := KeyName;
end;
end;
function GetSpecialShortcutNameWithShiftState(const AScanCode: Word; const AShiftState: System.Classes.TShiftState = []): string;
begin
Result := MyGetSpecialShortcutName(Vcl.Menus.ShortCut(AScanCode, AShiftState));
end;
Usage:
Result := GetSpecialShortcutNameWithShiftState(VK_A, [ssCTRL]);
However, the Result is "A" where the expected Result should be "CTRL+A".
How to get the string representation of a ShortCut Key including the SHIFTSTATE?
The OP wants the key names fully localised, but for completeness I first show that the VCL already has a function to obtain a partly unlocalised string, namely, ShortCutToText in the Menus unit:
ShortCutToText(ShortCut(Ord('A'), [ssShift, ssAlt]))
This returns Shift+Alt+A on all systems.
Now, using the Win32 function GetKeyNameText already mentioned in the Q, it is easy to obtain a fully localised shortcut string:
function GetKeyName(AKey: Integer): string;
var
name: array[0..128] of Char;
begin
FillChar(name, SizeOf(name), 0);
GetKeyNameText(MapVirtualKey(AKey, 0) shl 16, #name[0], Length(name));
Result := name;
end;
function ModifierVirtualKey(AModifier: Integer): Integer;
begin
case AModifier of
Ord(ssShift):
Result := VK_SHIFT;
Ord(ssCtrl):
Result := VK_CONTROL;
Ord(ssAlt):
Result := VK_MENU;
else
Result := 0;
end;
end;
function ShortcutToString(AKey: Integer; AShiftState: TShiftState = []): string;
begin
Result := '';
for var Modifier in AShiftState do
begin
var ModifierKey := ModifierVirtualKey(Ord(Modifier));
if ModifierKey <> 0 then
Result := Result + IfThen(not Result.IsEmpty, '+') + GetKeyName(ModifierKey);
end;
Result := Result + IfThen(not Result.IsEmpty, '+') + GetKeyName(AKey);
end;
(Here I use a IfThen overload from StrUtils.)
Now,
ShortcutToString(Ord('A'), [ssShift, ssAlt])
returns SKIFT+ALT+A on my Swedish system. SKIFT is, as you might already have guessed, the Swedish name for the SHIFT key.

how do i extract first string from specific text

i need to extract string from a text as following example
Hi i have no name <z>empty</z>
i wanted to extract only text before <z> into array or string which is hi i have no name
i tried this function
procedure Split (const Delimiter: Char; Input: string; const Strings: TStrings);
begin
Assert(Assigned(Strings)) ;
Strings.Clear;
Strings.StrictDelimiter := true;
Strings.Delimiter := Delimiter;
Strings.DelimitedText := Input;
end;
but its only can split chars like ;,: etc.. i wanted to start split with this specific string <z>
As I read what you have written, you have a string and you want to ignore all text after the first occurrence of <z>. Use Pos and Copy for instance:
P := Pos('<z>', input);
if P = 0 then
output := input
else
output := Copy(input, 1, P-1);
Although something tells me that you really want an XML parser.
Just an example using the string helper routines:
function CutString(const input,pattern: String): String;
var
p: Integer;
begin
p := input.IndexOf(pattern);
if (p >= 0) then
Result := input.Substring(0,p)
else
Result := input;
end;
var
s: string;
begin
s := 'Hi i have no name<z>empty</z>';
s := CutString(s,'<z>');
WriteLn(s); // Outputs: 'Hi i have no name'
end.
Here is a solution for the one-liner-fan, the crack-nuts-with-sledgehammer-lover, the regex-enthusiast, the waste-performance-ignorant and the xml-parser-grump:
TRegEx.Replace('Hi i have no name <z>empty</z>', '((^.*)(?=<z>)|(^.*)(?!<z>)).*', '$1');
If the delimiter will be there for sure, you can do:
Result:= Copy(S, 1, Pos(Delimiter, S) - 1);
Otherwise user Davids answer or this (shorter but bad performance):
Result:= Copy(S, 1, Pos(Delimiter, S + Delimiter) - 1);

Function to increment filename

I'm trying to make a function that can increment a filename.
If last char of the string is a number then increment it.
If last char is a letter then add _1 or _2 or _3(increment this also).
I have to be sure the filename is unique but i cannot use datetime inside filename because all the filenames must be <32 chars without extension.
EX: Apple_99.txt =>Apple_100
Ex: Apple_173 => Apple_174
EX: This_is_my_first_text.txt => This_is_my_first_text_1.txt
Ex: This_is_my_first_text_9.txt => This_is_my_first_text_10.txt
I need to use this in order to rename a file an then upload it to a ftp server.
I've found a function that can do something like this but it only works if the filename contains only uppercase.How can I modify this function in order to access lowercase an uppercase string?
Here is the function:
function IncStr(Str: String; Amount: Integer; Index: Integer = -1): String;
const
MIN_VAL = 65; // 'A'
MAX_VAL = 90; // 'Z'
var
Digit, ToAdd, ToCarry: Integer;
begin
if (Index = 0) and (Amount > 0) then
begin
Result := Char(MIN_VAL + Amount - 1) + Str;
Exit;
end;
if Index = -1 then Index := Length(Str);
ToCarry := 0;
Digit := Ord(Str[Index]);
while not (Digit in [MIN_VAL..MAX_VAL]) do
begin
Dec(Index);
Digit := Ord(Str[Index]);
end;
ToAdd := Digit + Amount;
while (ToAdd > MAX_VAL) do
begin
Dec(ToAdd, 26);
Inc(ToCarry);
end;
Result := Str;
Result[Index] := Char(ToAdd);
if (ToCarry > 0) then
Result := IncStr(Result, ToCarry, Index - 1);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
S: String; // holds string to increment
C: Integer; // amount to increment by
begin
// make sure that Edit1 starts with a valid character
// i.e. 'A' to 'Z'
S := Edit1.Text;
C := StrtoIntDef(Edit2.Text, 0);
// test it, place result in Edit3
Edit3.Text := IncStr(S, C);
{
Example data:
Edit1 := AAZ
Edit2 := 2
= Edit3 := ABB
Edit1 := BZY
Edit2 := 3
= Edit3 := CAB
Edit1 := ZZZ
Edit2 := 1
= Edit3 := AAAA
Edit1 := AA-AC
Edit2 := 3
= Edit3 := AA-AF
Edit1 := AA/Z
Edit2 := 5
= Edit3 := AB/E
... etc
Here's one to try too :-)
Edit1 := ZZZ
Edit2 := 264172
}
end;
Thank you!
Like so many programming problems, the key is to break the problem down into small pieces. First of all, let's write a function to decode the original file name into its constituent parts:
procedure DecodeFileName(const Input: string; out Stem, Ext: string; out Number: Integer);
var
P: Integer;
begin
Ext := TPath.GetExtension(Input);
Stem := TPath.GetFileNameWithoutExtension(Input);
Number := 0;
P := Stem.LastIndexOf('_');
if P = -1 then begin
exit;
end;
if TryStrToInt(Stem.Substring(P+1), Number) then begin
Stem := Stem.Substring(0, P);
end;
end;
The following demonstrates how this works:
DecodeFileName('test.txt', Stem, Ext, Number);
Writeln(Stem, ', ', Number, ', ', Ext);
DecodeFileName('test_dd.txt', Stem, Ext, Number);
Writeln(Stem, ', ', Number, ', ', Ext);
DecodeFileName('test_23.txt', Stem, Ext, Number);
Writeln(Stem, ', ', Number, ', ', Ext);
The output is:
test, 0, .txt
test_dd, 0, .txt
test, 23, .txt
So now you can make a new filename like this:
function IncrementedFileName(const FileName: string): string;
var
Stem, Ext: string;
Number: Integer;
begin
DecodeFileName(FileName, Stem, Ext, Number);
Result := Format('%s_%d%s', [Stem, Number+1, Ext]);
end;
And then we can see how that performs:
Writeln(IncrementedFileName('test.txt'));
Writeln(IncrementedFileName('test_dd.txt'));
Writeln(IncrementedFileName('test_23.txt'));
Writeln(IncrementedFileName('test_28'));
The output is:
test_1.txt
test_dd_1.txt
test_24.txt
test_29
If you don't have access to the string helper methods then you can code it like this:
procedure DecodeFileName(const Input: string; out Stem, Ext: string; out Number: Integer);
var
P: Integer;
begin
Ext := TPath.GetExtension(Input);
Stem := TPath.GetFileNameWithoutExtension(Input);
Number := 0;
P := LastDelimiter('_', Stem);
if P = 0 then begin
exit;
end;
if TryStrToInt(Copy(Stem, P+1, MaxInt), Number) then begin
Stem := Copy(Stem, 1, P-1);
end;
end;
I have not executed this final function, so do not be surprised if it has errors.

DELPHI STRING: Pull a last name from a full name

I am trying to manipulate a string and pull only certain data from it. I need to do this on a record pulled from a database that gives me the full name of a person. I need to pull only the last name from the string and store it as a variable. Is there a way that I can do this?
Example: SQL query pulls the full field "Mary Ellen Jones" I need to extract only the Jones from the string so I can store it in a variable for further processing.
I thought maybe AnsiRightStr would work but the problem is needing to give it a set integer to pull from the right. Maybe a way to count the characters after the final space allowing me to use AnsiRightStr(string,int) for this? Any help at all is appreciated.
Additional thought: Would replacing the spaces with a delimiter say :: and then parsing that data into a Stringlist followed by allowing me to pull the last index of the string list be possible?
Several valid options have been presented so far. None of them address the situation if say the name is Something like "John St. James, Jr." Is this impossible?
you can use the LastDelimiter function to get the last space position and then with the copy function extract the substring.
uses
SysUtils;
var
Name : string;
p : Integer;
ShortName : string;
begin
Name:='Mary Ellen Jones';
//You can call trim to avoid problems with ending spaces in this case is not necesary, just is a test
//Name:=Trim(Name);
//get the last space position
p:=LastDelimiter(' ',Name);
//get the name
ShortName:=Copy(Name,p+1,length(Name)-p);
end;
or using a function
function GetLast(const Name:string) : string;
var
p : Integer;
begin
Result:=Trim(Name);
p:=LastDelimiter(' ',Result);
Result:=Copy(Result,p+1,length(Result)-p);
end;
function GetLastWord(const Str: string): string;
var
p: integer;
i: Integer;
const
SPACE = #$20;
begin
p := 1;
for i := length(Str) downto 1 do
if Str[i] = SPACE then
begin
p := i + 1;
break;
end;
result := Copy(Str, p, MaxInt);
end;
This will fail if the string ends with (an accidental) space, as 'Andreas Rejbrand '. This more robust version will handle this case too:
function GetLastWord(const Str: string): string;
var
p: integer;
i: Integer;
FoundNonSpace: boolean;
const
SPACE = #$20;
begin
p := 1;
FoundNonSpace := false;
for i := length(Str) downto 1 do
if (Str[i] = SPACE) and FoundNonSpace then
begin
p := i + 1;
break
end
else if Str[i] <> SPACE then
FoundNonSpace := true;
result := TrimRight(Copy(Str, p, MaxInt));
end;
What if the last name is say "St. James" any way to account for that?
Here's my approach.
Make a list of lastname-markers
Search that list in order of preference
As soon as a match is found, mark that as the start of last name
Return substring starting from that pos.
var
LastNameMarkers: TStringList = nil;
SuffixFix: TStringList = nil;
procedure InitLists;
begin
LastNameMarkers:= TStringList.Create;
//LastNameMarkers.LoadFromFile('c:\markers.txt');
LastNameMarkers.Add(' St.');
LastnameMarkers.Add(' Mc');
LastNameMarkers.Add(' '); //Marker of last resort.
SuffixFix:= TStringList.Create;
SuffixFix.Add(' Jr.');
SuffixFix.Add(' Sr.');
end;
function GetLastName(FullName: string): string;
var
i: integer;
start: integer;
found: boolean;
ReplaceWith: string;
begin
if LastNameMarkers = nil then InitLists;
//Fix suffixes
i:= 0;
found:= false;
while (i < SuffixFix.Count) and not found do begin
start:= pos(lower(LastNameMarkers[i]),lower(Fullname));
found:= Start > 0;
Inc(i);
end; {while}
if Found then begin
Dec(i);
ReplaceWith:= StringReplace(Suffix[i], ' ', '_',[]);
FullName:= StringReplace(FullName, SuffixFix[i], ReplaceWith,[]);
end; {if}
//Look for lastnames
i:= 0;
found:= false;
while (i < LastNameMarkers.Count) and not found do begin
start:= pos(LastNameMarkers[i],Fullname);
found:= Start > 0;
Inc(i);
end; {while}
if found then Result:= RightStr(FullName, Length(FullName)- Start + 2)
else Result:= '';
StringReplace(Result, '_', ' ',[]);
end;
I haven't dealt with upper and lowercase properly, but I hope you get the idea.
function TfrmCal.GetLastName(FullName: string): string;
var
i: integer;
found: boolean;
suffix: string;
marker: string;
begin
// Build the lists for the compare.
InitLists;
// Look at Suffixes and attach them to the LastName
i := 0;
found := False;
while (i < SuffixFix.Count) do
begin
if AnsiContainsStr(FullName, SuffixFix[i]) then
begin
suffix := '::' + trim(SuffixFix[i]);
FullName := ReplaceStr(FullName, SuffixFix[i], suffix);
found := True;
end;
inc(i);
if found then
break;
end;
// Look for LastName Markers
i := 0;
found := False;
while (i < LastNameMarkers.Count) do
begin
if AnsiContainsStr(FullName, LastNameMarkers[i]) then
begin
marker := trimright(LastNameMarkers[i]) + '::';
FullName := ReplaceStr(FullName, LastNameMarkers[i], marker);
found := True;
end;
inc(i);
if found then
break;
end;
FullName := GetLastWord(FullName);
FullName := ReplaceStr(FullName, '::', ' ');
LastNameMarkers.Clear;
SuffixFix.Clear;
Result := FullName;
end;
function TfrmCal.GetLastWord(const Str: string): string;
var
p: integer;
i: integer;
const
SPACE = #$20;
begin
p := 1;
for i := Length(Str) downto 1 do
if Str[i] = SPACE then
begin
p := i + 1;
break;
end;
Result := Copy(Str, p, MaxInt);
end;
These two functions together pull off what I needed to do. There is also the initlists function which is clunky and ugly and I need to work on so I didn't post it here.

Enumerating a string

I have a status which is stored as a string of a set length, either in a file or a database.
I'm looking to enumerate the possible status'
I have the following type to define the possible status'
Type TStatus = (fsNormal = Ord('N'),fsEditedOnScreen = Ord('O'),
fsMissing = Ord('M'),fsEstimated = Ord('E'),fsSuspect = Ord('s'),
fsSuspectFromOnScreen = Ord('o'),fsSuspectMissing = Ord('m'),
fsSuspectEstimated = Ord('e'));
Firstly is this really a good idea? or should I have a seperate const array storing the char conversions? That would mean more than one place to update.
Now convert a string to a status array I have the following, but how can I check if a char is valid without looping through the enumeration?
Function StrToStatus(Value : String):TStatusArray;
var
i: Integer;
begin
if Trim(Value) = '' then
begin
SetLength(Result,0);
Exit;
end;
SetLength(Result,Length(Value));
for i := 1 to Length(Value) do
begin
Result[i] := TStatus(Value[i]); // I don't think this line is safe.
end;
end;
After some testing it sames the suspect line is safe (it doesn't crash!) but just adds in (out of bounds) values which then need filtering out.
Function StrToStatus(Value : String):TStatusArray;
var
i: Integer;
begin
if Trim(Value) = '' then
begin
SetLength(Result,0);
Exit;
end;
SetLength(Result,Length(Value));
for i := 1 to Length(Value) do
begin
Result[i-1] := TStatus(Value[i]);
end;
for i := 0 to Length(Result) - 1 do
begin
case Result[i] of
fsNormal: ;
fsEditedOnScreen: ;
fsMissing: ;
fsEstimated: ;
fsSuspect: ;
fsSuspectFromOnScreen: ;
fsSuspectMissing: ;
fsSuspectEstimated: ;
else
Result [i] := fsNormal;
end;
end;
end;
This allows all the status' and their relative Char values to be in one place and prevents looping through every status for every character in the string. (So in my head atleast should be a bit faster)
AFAIK this should be fine for converting back again.
Function StatusToStr(Value : TStatusArray):String;
var
i: Integer;
begin
for i := 0 to Length(Value) - 1 do
Result := Result + Chr(Ord(Value[i]))
end;
I'm using Delphi 2007
If I understand you correctly I would replace the array with a set and use an enum without explicit values, like so:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TStatus = (fsNormal, fsEditedOnScreen, fsMissing, fsEstimated, fsSuspect,
fsSuspectFromOnScreen, fsSuspectMissing, fsSuspectEstimated);
TStatusSet = set of TStatus;
const
cStatusChars: array[TStatus] of Char = ('N', 'O', 'M', 'E', 's', 'o', 'm', 'e');
function CharToStatus(AChar: Char; out AStatus: TStatus): Boolean;
var
st: TStatus;
begin
for st := Low(TStatus) to High(TStatus) do
if cStatusChars[st] = AChar then
begin
AStatus := st;
Result := True;
Exit;
end;
Result := False;
end;
function StrToStatus(const Value: string): TStatusSet;
var
i: Integer;
st: TStatus;
begin
Result := [];
for i := 1 to Length(Value) do
if CharToStatus(Value[i], st) then
Include(Result, st);
end;
function StatusToStr(const Value: TStatusSet): string;
var
st: TStatus;
begin
for st in Value do
Result := Result + cStatusChars[st];
end;
var
StatusSet: TStatusSet;
begin
StatusSet := StrToStatus('EmO');
Writeln(StatusToStr(StatusSet));
Readln;
end.
First, I wonder why you save it as string instead of as integer.
The way you've done it, the only way to do it correctly would be to have a Case condition...
function CharToStatus(AChar : Char):TStatus;
begin
case AChar of
'N' : Result := fsNormal;
'O' : Result := fsEditedOnScreen;
'M' : Result := fsMissing;
'E' : Result := fsEstimated;
's' : Result := fsSuspect;
'o' : Result := fsSuspectFromOnScreen;
'm' : Result := fsSuspectMissing;
'e' : Result := fsSuspectEstimated;
else
//Manage error;
end;
end;
function StatusToChar(AStatus : TStatus) : char;
begin
Result := Char(AStatus);
end;
The expression x in [Low(TStatus)]..High(Tstatus)] won't work in this situation.
The reason for this is that Low(TStatus) = 'E', and High(TStatus) = 's'. Anything in-between would be considered valid. (i.e. 'Z' is in [Low(TStatus)]..High(Tstatus)])
The expression x in [Low(TStatus)]..High(Tstatus)] only work on type where there is no "hole" in the declaration. (Like those without explicit values, where the first element is 0, 2nd is 1, 3rd is 2... etc)
//EDIT
Ok.. thinking the problem a bit further, I don't see why you don't like the const array approach... Something like this would be a lot better.
type
TStatus = (fsNormal, fsEditedOnScreen,
fsMissing,fsEstimated,fsSuspect,
fsSuspectFromOnScreen,fsSuspectMissing ,
fsSuspectEstimated);
const
StatusValue : Array[TStatus] of Char = ('N','O','M','E','s','o','m','e');
function StatusValueToTStatus(C : Char) : TStatus;
var I : Integer;
begin
for I := Low(StatusValue) to High(StatusValue) do
begin
if StatusValue = C then
begin
Result := TStatus(I);
EXIT;
end;
end;
//Not found, Manage errors
end;

Resources