'' Is Not a Valid Identifier (Delphi XE5) - string

I'm new to Delphi XE5 (part of Embarcadero's RAD Studio), and using the StrToFloat function in a VCL Forms project designed to build a simple calculator.
The code to display the text to the user works well, but I'm having trouble extracting it in a way that will allow the calculator to actually perform calculations. The means I've settled on is displaying the code to the user in a Tedit, and storing the code to be executed in a Tmemo. Pressing the buttons on the calculator stores the numbers to both, and pressing an operator (the symbols for add, subtract, exponent, multiply, and divide) starts a new line in the memo. This way, each number is on its own line, and each operator is on its own line.
I then want to extract the numbers with respect to the operators, by iterating through the lines of the memo. For 3 ^ 4, you would see...
3
^
4
...in the TMemo.
I want to find the ^ sign at line index 1 (lines start at index 0, right?), and store 3 in a base variable by the logic that, if ^ is lines[1], then 3 is lines[1-1], or lines[0], and store 4 in an exponent variable. Both variables would be of type extended. This is the code I've created to do this...
procedure TForm1.btnEqualsClick(Sender: TObject);
var
i: Integer;
base: extended;
exponent: extended;
result: extended;
begin
{This For loop looks for the exponentiation operator (^) in memCurrentEntry. If
it's found, it then solves the equation.}
for i := Low(memCurrentEntry.Lines.Text.Length) to High(memCurrentEntry.Lines.Text.Length) do
if AnsiContainsStr(memCurrentEntry.Lines.Text, '^') then
begin
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
exponent := StrToFloat(memCurrentEntry.Lines[ansiPos('^', memCurrentEntry.Lines.Text)+2]);
result := power(base, exponent);
end;
When I run the calculator, the numbers appear correctly, but when I hit the equals button to run the code above, I get the message Project Calculator_u.exe raised exception class EConvertError with Message ''' is not a valid floating point value'.
I know the StrToFloat function is trying to convert a null character or something (How do I identify what code is generating " '' is not a valid floating point value" error dialogue box), but what should I do to correct this?
Below is the code for each button -- the digit (0, 1, 2, 3, etc.) changes, but the rest is the same for each one...
procedure TForm1.btn0Click(Sender: TObject);
begin
//Adds the digit 0 to the end of the current number in memCurrentEntry
memCurrentEntry.SelStart := Length(memCurrentEntry.Text);
memCurrentEntry.Lines.Text := Copy(memCurrentEntry.Lines.Text,1,
memCurrentEntry.SelStart)+'0'+ Copy(memCurrentEntry.Lines.Text,
memCurrentEntry.SelStart+1,Length(memCurrentEntry.Lines.Text)
- memCurrentEntry.SelStart);
//Adds the digit 0 to the end of the current number in edtCurrentEntry
edtcurrententry.Text := edtcurrententry.Text + '0';
end;
I can post the full unit file upon request.

Use TryStrToFloat. This returns a boolean indicating whether or not the conversion succeeded.
var
str: string;
val: Double;
....
if TryStrToFloat(str, val) then
// val contains the floating point value represented by str
else
// str does not contain a floating point value, do something else with it
Of course, you could use StrToFloat, and catch the exception. But since you expect to encounter the exception, it will result in much cleaner code if you use TryStrToFloat.

the usage of lines on the memo is wrong:
base := StrToFloat(memCurrentEntry.Lines[ansipos('^', memCurrentEntry.Lines.Text)-1]);
the lines.text returns the ENTIRE text, which is not what you want. Instead use some LOCAL variables to hold your lookup values...that way you can set a breakpoint and see whats wrong and use the following method to locate the "^"
result := 0;
carrotLine := memCurrentEntry.Lines.IndexOf('^');
if carrotline > 0 then // must be 1 or greater, so a number can be at the 0 position
begin
base := TryStrToFloat(MemCurrentEntry.Lines[carrotLine-1],0);
exponent := 0;
// only try to read the next value if there is one.
if memCurrentEntry.Lines.Count >= carrotLine then
exponent := TryStrToFloat(MemCurrentEntry.Lines[carrotline+1],0);
result := power(base,exponent);
end;
Note that this code ONLY handles a single ^ in the memo (your code had the same issue), to go beyond this is to use a loop and possibly a state engine or some other parsing logic to handle computations.

Use StrToFloat is not wrong. If the conversion is failed, user should see an error message. If you want to suppress the error quietly, then you should add a try/except to protect your code.
try
Value := StrToFloat(A_String);
except
on EConvertError do
Value := 0; // Fallback to a default value or execute other code logic
end;
According to your given code example, if the base and the exponent are entered in your given format (first line is the base, second line is a "^" and the third line is the exponent). I'd suggest you write your code as follows:
var
Numbers: TArray<string>;
// Other variables go here
begin
// ...
Numbers := memCurrentEntry.Lines.Text.Split('^');
if Length(Numbers) = 2 then
try
Base := StrToFloat(Numbers[0]);
Exponent := StrToFloat(Numbers[1]);
Result := Power(Base, Exponent);
except
on EConvertError do
begin
ShowMessage(Format('Unable to calculate the formula %s', [memCurrentEntry.Lines.Text]));
Result := 0; // Fallback to a default value, such as 0
end;
end;
// ...
end;

Related

Delphi Comport Carriage Return and Line Feed

I've designed a little project reads from Comport with ComPort Library version 4.10. When i tested with com0com emulator it works perfectly but when i read data from real machine (it's an electronic test machine sending data via RS232) data comes with CR and LF,i've searched alot and tried all procedures to clear CR&LF but failed.
Here are my codes;
var veri:string;
begin
panel1.Color:=clGreen;
panel1.Caption:='VERİ OKUNUYOR';
veri:=Trim(Str);
memo1.lines.add(CleanupString(trim(veri)));
I've tried to remove #13,#13#10,#13+#10,#32,sLineBreak and tried below functions;
function CleanupString(S :string) :string;
var
i :integer;
begin
Result := S;
for i := 1 to Length(Result) do
if Result[i] in [#3..#13] then
Result[i] := '_';
end;
function RemoveCRLF (const aString : string) : string;
var i, j : integer;
begin
result := aString;
j := 0;
for i := 1 to Length (result) do
begin
if (not (result [i] in [#10, #13]))
then begin
inc (j);
result [j] := result [i];
end;
end;
SetLength (result, j);
end;
But no luck so far, below image shows what my memo content looks like;
Device sending data starting with "APSiS0BE" and seems like sending CR&LF with every parameter but i couldn't be able to get data in one line.
I don't think that you are seeing what you believe you are seeing.
When you add a line to TMemo, you implicitly add a CR+LF, so you are removing them, but then adding them back in. If you were not, when you open the contents in Notepad++ as you do, there would be empty blank lines, and you are not seeing them.
So, I think you are successfully removing the CR+LF, you just don't realise it.
Delphi has the Trim method which is built in and will remove leading and trailing whitespace and control characters (CRLF, etc). Your method removes them from anywhere in the string but for RS-232 protocols often Trim is all you would typically need.
That said, the TMemo.lines.Add method adds a line to the memo... with a CRLF, so if you're examining the memo output it will naturally have line ending characters in it. Without it the text would just continue on a single line.
I use ComPort too and I use StringReplace to remove CR + LF :
S:= StringReplace(C, sLineBreak, '', [rfReplaceAll]);
StringReplace

Inno Setup loop from A to Z

I am trying to write a function that will check for the existence of a particular directory on each drive in turn, store the path where it is found as a variable and return true if it exists and false otherwise:
function IsDirPresent(): Boolean;
var
chrDriveLetter: Char;
strMyDir: String;
begin
for chrDriveLetter := 'A' to 'Z' do
begin
if DirExists(chrDriveLetter + ':\MyDir') then
strMyDirPath := chrDriveLetter + ':\MyDir'
end;
if strMyDirPath <> '' then
Result := True;
end;
I get a Type mismatch error on my loop line when compiling this. I am struggling to work out what is wrong with the loop and haven't been able to find any information on how to correctly create a loop from A to Z in Inno Setup.
Your loop is valid for Pascal language because the Char type is ordinal and its range constants are of the same type, so I guess this is a specific limitation of the Pascal Script language notation. I don't know if there's some syntax workaround nor if the Char type is even allowed to be used for the loop control variable.
I can only provide you a workaround working with integer type loop control variable. It uses the Ord and Chr functions. The first returns the ordinal value of a char, the latter ASCII char from the ordinal value:
var
C: Char;
I: Integer;
begin
for I := Ord('A') to Ord('Z') do
begin
C := Chr(I);
...
end;
end;

How do I create a random string in Free Pascal?

I want to write a function GenString that returns a random string of length [0 .. 100] based on a parameterized function GenChar that users could customize.
That is, if one user wants to generate a string of all as, he could do that by passing in a function that always returns a, and another user could generate random alphabetic Hebrew words by passing in a custom GenChar function that returns random Hebrew letters.
Lemma 1: I want to write a function GenArray that generates an array of whatever type a passed-in function GenThing : sometype returns. Could this be done in Free Pascal.
Lemma 2: What I'm trying to do is port QuickCheck to Free Pascal.
Here's an example. Unicode string is used since international characters is mentioned.
//The functions
type
TGenChar = function: widechar;
function GenString(MaxLength: integer; GenChar: TGenChar): widestring;
var
i: integer;
begin
if MaxLength < 1 then
MaxLength:= 1; //minimum length is 1
setlength(result, random(MaxLength+1));
for i:= 1 to length(result) do
result[i]:= GenChar;
end;
//The usage example (remember to call randomize somewhere in your program)
var
SampleCharSet: widestring= 'ABCxyz';
function SampleGenChar: widechar;
begin
result:= SampleCharSet[random(length(SampleCharSet))+1];
end;
function GetRandomStringSample: widestring;
begin
result:= GenString(100, SampleGenChar);
end;

OleVariant and String assignments in Delphi 5

I have a Delphi 5 legacy application and there's a part in which a "string" value is been assigned to an "OleVariant" variable. Something like this:
var
X: OleVariant;
S: string;
Begin
S:= ‘This string should contain 200 characters as per design’;
X:= S;
End;
If the length of “S” is greater than 128, then the value of “X” gets truncated and it only holds a maximum of 128 characters.
Is there a way to overcome this?
I believe there is a way, because if I create my own demo application from scratch (in the same PC, with the same Delphi 5), it allows me to pass longer string values and no truncating is done.
Maybe it is something about the project settings or compiler directives. I have played around with this idea, but I have no workaround yet.
Any help is appreciated. Thanks.
Demo:
procedure TForm1.Button1Click(Sender: TObject);
var
X: OleVariant;
S: string;
begin
//in the Edit I pass a string of 240 chars, let's say;
S:= Edit1.Text;
X:= S;
ShowMessage(IntToStr(Length(X)) + ' : ' + IntToStr(Length(S)));
//this showmessage shows "128 : 240"
end;
Try this OleVariantToString and StringToOleVariant functions at http://www.foxbase.ru/delphi/vzaimnye-preobrazovaniya-olevariant-i-string.htm
They work perfectly for me.
uses Classes, Variants;
function OleVariantToString(const Value: OleVariant): string;
var ss: TStringStream;
Size: integer;
Data: PByteArray;
begin
Result:='';
if Length(Value) = 0 then Exit;
ss:=TStringStream.Create;
try
Size := VarArrayHighBound (Value, 1) - VarArrayLowBound(Value, 1) + 1;
Data := VarArrayLock(Value);
try
ss.Position := 0;
ss.WriteBuffer(Data^, Size);
ss.Position := 0;
Result:=ss.DataString;
finally
VarArrayUnlock(Value);
end;
finally
ss.Free;
end;
end;
function StringToOleVariant(const Value: string): OleVariant;
var Data: PByteArray;
ss: TStringStream;
begin
Result:=null;
if Value='' then Exit;
ss:=TStringStream.Create(Value);
try
Result := VarArrayCreate ([0, ss.Size - 1], varByte);
Data := VarArrayLock(Result);
try
ss.Position := 0;
ss.ReadBuffer(Data^, ss.Size);
finally
VarArrayUnlock(Result);
end;
finally
ss.Free;
end;
end;
One explanation is that OleVariant holds the entire string but that you are looking at the debugger tooltip. In older Delphi versions the debugger tooltip truncates at 128 characters for strings held in a variant. Note that the debugger tooltip for a plain string does not truncate at this length. Try showing the variant in a dialog box and you will see that the entire string is present.
I checked this out on Delphi 6 and there was no truncation with your code (other than the debugger tooltip). Andreas did likewise on Delphi 4 and Rodrigo did so with Delphi 5. I cannot imagine that it could really be the case that strings in a Delphi 5 OleVariant are truncated at 128 characters.
If you really are seeing what you are report then I can think of the following explanations:
Your code is erroneously truncating the string, but you have not yet found the code that does this. Only you can debug that.
You have a local bug private to your Delphi installation. Are you by any chance compiling your own RTL?
I made this work. Summary: instead of filling an “OleVariant” with a “string”; I filled a “Variant” and then typecasted that “Variant” to “OleVariant”. Take a look at the code below so that you can get the idea.
procedure TForm1.Button1Click(Sender: TObject);
var
//X: OleVariant;
X: Variant;
S: string;
begin
//Let's say in the Edit1 I pass a string of 240 chars,
S:= Edit1.Text;
X:= S;
//ShowMessage(IntToStr(Length(X)) + ' : ' + IntToStr(Length(S)));
ShowMessage(IntToStr(Length(OleVariant(X))) + ' : ' + IntToStr(Length(S)));
//This ShowMessage shows "128 : 240"
end;
Honestly, I don’t know for sure why this makes a difference, but it does. It works ok now.
Thanks a lot for your help folks!

Inno Setup comma error

I have GetVersion function in [Code] that returns a string like this "1004", "1003", etc.
I created this function to check the registry value for lowest version numbers and uninstall them.
Here is a snippet of the code it is giving error point to StrtoInt conversion line stating
Comma (,) expected
Here is the snippet:
function DoesOldVersionsExist(): Boolean;
var
AppVersion: integer;
mstr: string;
VersionInstalled: cardinal;
begin
AppVersion := StrToInt(GetVersion({#MyAppVersion}), 0);
...
after that line I'm simple comparing the values and return true or false. Much Appreciated.
This is what error message says:
Line 55
Column 40.
Comma (,) expected
Thanks Deanna but unfortunately that is the error message pointing to this :
AppVersion := StrToInt(GetVersion({#MyAppVersion}), 0);
^
Here is the GetVersion function:
function GetVersion(AppVersion: String): String;
var
Version: String;
CharIndex: integer;
c: char;
begin
for CharIndex := 1 to Length(AppVersion) do begin
c := AppVersion[CharIndex];
if (c <> '.') then
Version := Version + c;
end;
Result := Version;
end;
I think that you can't just have Inno Setup constants in code like this, you have to use ExpandConstant():
AppVersion := StrToInt(GetVersion(ExpandConstant('{#MyAppVersion}')), 0);
You have not given us enough information to give a definite answer, but I think that the situation is as follows.
You have defined some constant called MyAppVersion which you let the ISPP (the Inno Setup pre-processor) substitute. Now, you have not told us what type this variable is, and you have not told us what the signature of GetVersion is (in particular, what type of argument does it expect?). However, if these types are strings, you need to write
StrToInt(GetVersion('{#MyAppVersion}'), 0);
in order to obtain, say,
StrToInt(GetVersion('Some string, this is!'), 0);
instead of
StrToInt(GetVersion(Some string, this is!), 0);
which is malformed (indeed, to such an extent that it hurts my eyes to look at it).

Resources