how to validate input with "real" as the variable to numbers only? - pascal

im a coding newbie, currently using freepascal to learn. i want to make a simple temperature converter. im struggling at how to validate the input. because im using "real" as var, and i cant validate the input to numbers and negative sign only. i want to write "false input" and repeat to temperature input, if the input contains alphabet/alphanumeric. but when i input the temperature in alphabet/alphanumeric it will just exiting from program. how to do this correctly? is there any solution?
input validation with "real" as the variable
here's my current code
program Fahrenheit_to_Celsius;
uses crt;
function strtofloat (floatstring : string) : extended;
var F: string;
floatvalue: extended;
C: real;
exit: boolean;
decision: shortstring;
begin
clrscr;
textattr :=$3;
gotoxy (52,1);
writeln ('Fahrenheit to Celsius Converter');
repeat
write ('Input Fahrenheit: ');
readln (F);
begin
try
F := strtofloat ('10 E 2');
except
on exception : econverterror do
showmessage (exception.message);
end;
try
F := strtofloat ('$FF');
except
on exception : econverterror do
showmessage (exception.message);
end;
C := ((F-32)*(5/9));
writeln ('Temperature in Celsius: ', C:0:5);
begin
writeln;
gotoxy ((wherex () + 56), (wherey() - 0));
writeln ('Convert again or exit?');
gotoxy ((wherex () + 31), (wherey() - 0));
writeln ('Input e then press enter to exit or input anything else to convert again');
write ('Decision:');
readln (decision);
if (decision = 'e') then
exit := true
else
exit := false;
gotoxy ((wherex () + 0), (wherey() - 3));
delline;
gotoxy ((wherex () + 0), (wherey() - 0));
delline;
end;
until exit;
end.

It's been a long time since I practiced pascal but I will give you an idea, I think you cannot validate it this way because if you declare the input as real once the program recive an non valid input it exits, so what I suggest, is to declare the variable as string instead, then you create a function to validate it, finally if the function returns true you convert the string to float.
The function should loop through every character in the recieved string and returns false if
one character is not a number or not a .
the string contains more then one . ( create a variable and increment it every time you catch a character equal to . )
the first character is .
the last character is .

Related

Wrong use of 'file of char'

Im having problems with this code, I have two file of char, one is filed with information about books, and the other is empty, i have to write in SAL some information from S and then show the total of how many books match the first 2 digits of the code and how many are R and how many are T. The code, does write the information form S to Sal, but when its supposed to show the totals it appears ERORR 100 on screen. I read about it and it says that it is a problem with 'Disk read error' and that *This error typically occurs, if you "seed" a non-existent record of a typed file and try to read/write it. *, i really dont undertand.
I've benn trying to figure it out, but I haven't been able to. I notice that if I dont put 'WHILE NOT EOF(S) DO' the error does not appear, but of course i need the while, if someone is able to point out my mistakes i would really apreciate it.
This is the code:
uses crt;
var
i : byte;
s,sal: file of char;
v,l1,l2: char;
cs,cn,cl: integer;
pn,ps,tot: integer;
BEGIN
cs:=0; cn:=0; i:=0; cl:=0;
Assign (s, 'C:\Users\te\Documents\s.txt');
{$I-}
Reset (s);
{$I+}
if IOResult <> 0 then
begin
writeln('Error');
halt(2);
end;
Assign (sal, 'C:\Users\te\Documents\sal.txt');
{$I-}
Rewrite (sal);
IOResult;
{$I+}
if IOResult <> 0 then
halt(2);
writeln('Please write the code of the book, only 2 digits');
read(L1);read(L2);
read(s,v);
while (not eof(s)) do
begin
for i:=1 to 2 do
read(s,v);
if (v = '0') then
begin
read(s,v);
if (v = '1') or (v = '2') then
begin
for i:=1 to 5 do
read(s,v);
if (v = 'R') then
begin
read(s,v);
cs:= cs + 1;
end
else
begin
if (v = 'T') then
begin
cn:= cn + 1;
read(s,v);
end;
end;
while (v <> '-') do
read(s,v);
while (v = '-') do
read(s,v);
if (v = L1) then
begin
write(sal, v);
read(s,v);
if (v = L2) then
begin
write(sal,v);
read(s,v);
cl:= cl + 1;
end;
end;
while ( v <> '/') do
begin
write(sal,v);
read(s,v);
end;
write(sal, '-');
end
else
begin
for i:= 1 to 5 do
read(s,v);
if (v = 'R') then
cs:= cs + 1
else
cn:= cn + 1;
if (v = L1) then
read(s,v);
if (v = L2) then
begin
cl:= cl + 1;
read(s,v);
end;
end;
end
else
begin
for i:= 1 to 5 do
read(s,v);
if (v = 'R') then
cs:= cs + 1
else
cn:= cn + 1;
if (v = L1) then
read(s,v);
if (v = L2) then
begin
cl:= cl + 1;
read(s,v);
end;
end;
end;
tot:= cs + cn;
ps:= (cs * 100) div tot;
pn:= (cn * 100) div tot;
writeln('TOTAL ',cl);
writeln();
writeln(ps,'% and',pn,'%');
The file S content:
02022013Rto kill a mockingbird-1301/02012014Tpeter pan-1001/02032013Thowto-2301/02012012Tmaze runner-1001/02012012Tmaze runner-1001/02012012Tmaze runner-1001/$
I really just need someone else's point of view on this code, I think maybe the algorithm is flawed.
Thanks
(After your edit, i see that your code now compiles w/o error in FPC, so I'm glad you've managed to fix the error yourself)
As this is obviously coursework, I'm not going to fix your code for you and in any case the wayEven so, I'm afraid you are going about this is completely wrong.
Basically, the main thing wrong with your code is that you are trying to control what happens as your read the source file character by character. Quite frankly, that's a hopeless way of trying to do it, because it makes the execution flow unnecessarily complicated and littered with ifs, buts and loops. It also requires you to keep mental track of what you are trying to do at any given step, and the resulting code is inherently not self-documenting - imagine if you came back to your code in six months, could you tell at a glance how it works and what it does? I certsinly couldn't personally.
You need to break the task down in a different way. Instead of analysing the problem from the bottom up ("If I read this character next, then what I need to do next is ...') do it from the top down: Although your input file is a file of char, it contains a series of strings, separated by a / character and finally terminated by a $ (but this terminator does not really matter). So what you need to do is to read these strings one-by-one; once you've got one, check whether it's the one you're looking for: if it is. process it however you need to, otherwise read the next one until you reach the end of the file.
Once you have successfully read one of the book strings, you can then split it up into the various fields it's composed of. The most useful function for doing this splitting is probably Copy, which lets you extract substrings from a string - look it up in the FPC help. I've included functions ExtractTitle and ExtractPreamble which show you what you need to do to write similar functions to extract the T/R code and the numeric code which follows the hyphen. Btw, if you need to ask a similar q in the future, it would be very helpful if you include a description of the layout and meaning of the various fields in the file.
So, what I'm going to show you is how to read the series of strings in your S.Txt by building them character-by-character. In the code below, I do this using a function GetNextBook which I hope is reasonable self-explanatory. The code uses this function in a while loop to fill the BookRecord string variable. Then, it simply writes the BookRecord to the console. What your code should do, of course, is to process the BookRecord contents to see if it is the one you are looking for and then do whether the remainder of your task is.
I hope you will agree that the code below is a lot clearer, a lot shorter and will be a lot easier to extend in future than the code in your q. They key to structuring a program this way is to break the program's task into a series of functions and procedures which each perform a single sub-task. Writing the program that way makes it easier to "re-wire" the program to change what it does, without having to rewrite the innards of the functions/procedures.
program fileofcharproject;
uses crt;
const
sContents = '02022013Rto kill a mockingbird-1301/02012014Tpeter pan-1001/02032013Thowto-2301/02012012Tmaze runner-1001/02012012Tmaze runner-1001/02012012Tmaze runner-1001/$';
InputFileName = 'C:\Users\MA\Documents\S.Txt';
OutputFileName = 'C:\Users\MA\Documents\Sal.Txt';
type
CharFile = File of Char; // this is to permit a file of char to be used
// as a parameter to a function/procedure
function GetNextBook(var S : CharFile) : String;
var
InputChar : Char;
begin
Result := '';
InputChar := Chr(0);
while not Eof(S) do begin
Read(S, InputChar);
// next, check that the char we've read is not a '/'
// if it is a '/' then exit this while loop
if (InputChar <> '/') then
Result := Result + InputChar
else
Break;
end;
end;
function ExtractBookTitle(BookRecord : String) : String;
var
p : Integer;
begin
Result := Copy(BookRecord, 10, Length(BookRecord));
p := Pos('-', Result);
if p > 0 then
Result := Copy(Result, 1, p - 1);
end;
procedure AddToOutputFile(var OutputFile : CharFile; BookRecord : String);
var
i : Integer;
begin
for i := 1 to Length(BookRecord) do
write(OutputFile, BookRecord[i]);
write(OutputFile, '/');
end;
function ExtractPreamble(BookRecord : String) : String;
begin
Result := Copy(BookRecord, 1, 8);
end;
function TitleMatches(PartialTitle, BookRecord : String) : Boolean;
begin
Result := Pos(PartialTitle, ExtractBookTitle(BookRecord)) > 0;
end;
var
i : Integer; //byte;
s,sal: file of char;
l1,l2: char;
InputChar : Char;
BookFound : Boolean;
cs,cn,cl: integer;
pn,ps,tot: integer;
Contents : String;
BookRecord : String;
PartialTitle : String;
begin
// First, create S.Txt so we don't have to make any assumptions about
// its contents
Contents := sContents;
Assign(s, InputFileName);
Rewrite(s);
for i := 1 to Length(Contents) do begin
write(s, Contents[i]); // writes the i'th character of Contents to the file
end;
Close(s);
cs:=0; cn:=0; i:=0; cl:=0;
// Open the input file
Assign (s, InputFileName);
{$I-}
Reset (s);
{$I+}
if IOResult <> 0 then
begin
writeln('Error');
halt(2);
end;
// Open the output file
Assign (sal, OutputFileName);
{$I-}
Rewrite (sal);
IOResult;
{$I+}
if IOResult <> 0 then
halt(2);
// the following reads the BookRecords one-by-one and copies
// any of them which match the partial title to sal.txt
writeln('Enter part of a book title, followed by [Enter]');
readln(PartialTitle);
while not Eof(s) do begin
BookRecord := GetNextBook(S);
writeln(BookRecord);
writeln('Preamble : ', ExtractPreamble(BookRecord));
writeln('Title : ', ExtractBookTitle(BookRecord));
if TitleMatches(PartialTitle, BookRecord) then
AddToOutputFile(sal, BookRecord);
end;
// add file '$' to sal.txt
write(sal, '$');
Close(sal);
Close(s);
writeln('Done, press any key');
readln;
end.

How to forbid equal numbers

I started learning Pascal :) and I was interested on making a kind of Euromillion... However, I don't know how to forbid the same numbers or stars...
I thought this (below) would solve it... But it didn't... Help?
Program euromillion;
var num: array [1..5] of integer;
Procedure numbers;
var i, j: integer;
Begin
write ('Digite o número 1: ');
readln (num[1]);
for i:=2 to 5 do
for j:=1 to (i-1) do
Begin
repeat
write ('Digite o número ', i, ': ');
readln (num[i]);
until (num[i]>=1) and (num[i]<=50) and ((num[i]=num[j])=false);
End;
End;
Begin
numbers;
readln();
End.
Thanks guys :)
Although it is tempting to try and write a single block of code, as you have, it is better not to. Instead, a better way to write a program like this
is to think about splitting the task up into a number of procedures or functions
each of which only does a single part of the task.
One way to look at your task is to split it up into sub-tasks, as follows:
You prompt the user to enter a series of numbers
Once each number is entered, you check whether it is already in the array
If it isn't, you enter it in the array, otherwise prompt the user for another number
Once the array is filled, you output the numbers in the array
So, a key thing is that it would be helpful to have a function that checks whether
a new number is already in the array and returns True if it is and False otherwise. How to do that is the answer to your question.
You need to be careful about this because if you use the array a second time in the
program, you need to avoid comparing the new number with the array contents from
the previous time. I deliberately have not solved that problem in the example code below, to leave it as an exercise for the reader. Hint: One way would be to write a procedure which "clears" the array before each use of it, e.g. by filling it with numbers which are not valid lottery numbers, like negative numbers or zero. Another way would be to define a record which includes the NumberArray and a Count field which records how many numbers have been entered so far: this would avoid comparing the new number to all the elements in the
array and allow you to re-use the array by resetting the Count field to zero before calling ReadNumbers.
program LotteryNumbers;
uses crt;
type
TNumberArray = array[1..5] of Integer;
var
Numbers : TNumberArray;
Number : Integer;
function IsInArray(Number : Integer; Numbers : TNumberArray) : Boolean;
var
i : Integer;
begin
Result := False;
for i:= Low(Numbers) to High(Numbers) do begin
if Numbers[i] = Number then begin
Result := True;
break;
end;
end
end;
procedure ReadNumbers(var Numbers : TNumberArray);
var
i : Integer;
NewNumber : Integer;
OK : Boolean;
begin
// Note: This function needs to have a check added to it that the number
// the user enters is a valid lottery number, in other words that the
// number is between 1 and the highest ball number in the lottery
for i := Low(Numbers) to High(Numbers) do begin
repeat
OK := False;
writeln('enter a number');
ReadLn(NewNumber);
OK := not IsInArray(NewNumber, Numbers);
if not OK then
writeln('Sorry, you''ve already chosen ', NewNumber);
until OK;
Numbers[i] := NewNumber;
end;
end;
procedure ListNumbers(Numbers : TNumberArray);
var
i : Integer;
begin
for i := Low(Numbers) to High(Numbers) do
writeln(Numbers[i]);
end;
begin
ReadNumbers(Numbers);
ListNumbers(Numbers);
writeln('press any key');
readkey;
end.

Separating numbers in a string. Pascal

I have a problem. I'm learning Pascal for only a couple of weeks and I don't know much. I have to write a program that has to calculate something out of 3 entered numbers. The problem is all 3 of them need to be entered in one edit with spaces in between. So basically I have a string 'number number number'. How do I separate these numbers as 3 separate strings so I can convert them into Integer.
In pascal there are built-in procedures to retrieve the input from the console.
The easiest way to get numeric inputs is to use Read()/ReadLn(), which also can make the conversion from string to a numeric value:
procedure GetNumbers(var x,y,z: Integer);
begin
WriteLn('Enter three numbers separated with space and then press enter.');
ReadLn(x,y,z);
end;
Here, the ReadLn() detects three inputs separated with a space, waits for the [Enter] key and assigns the integer values to the x,y,z variables.
Using the copy function is one way. Sorry about the formatting, I can't understand how to paste code snippets properly in these answer sections.
function TMyForm.Add( anEdit : TEdit ) : integer;
var
Idx : integer;
TempString : string;
function GetNext : integer;
begin
result := result + StrToInt( copy( TempString, 1, Idx - 1 ) );
TempString := copy( TempString, Idx + 1, MAXINT );
end;
begin
result := 0;
TempString := anEdit.Text;
repeat
Idx := pos( ' ', TempString );
if Idx > 0 then
result := GetNext;
until Idx = 0;
if trim( TempString ) <> '' then
//this is the last piece of it then
result := result + StrToInt( trim( TempString ) );
end;
You need to also take care that the values entered are numbers and not letters, usually done with try..except blocks.

Command Line Args in pascal

I am new in pascal I have a program which gives result ....
I need to pass command line input in in given variable ip1 and ip2.
Its can be achieved by ParamStr[1] but it doesn't work.
program main;
var
output : integer;
var
ip1 : integer;
var
ip2 : integer;
function add(input1,input2:integer) : integer;
var
result: integer;
begin
if (input1 > input2) then
result := input1
else
result := input2;
add := result;
end;
begin
ip1 := 2533;**{ command line input}**
ip2 := 555;**{ command line input}**
output := add(ip1,ip2);
writeln( ' output : ', output );
end.K
As the other answer says, you use ParamCount and ParamStr to access command line parameters.
ParamCount returns the number of parameters passed on the command line, so you should check it first to see if you've received enough information.
ParamStr allows you to access each of the parameters passed. ParamStr(0) always gives you the full name of the executing program (including the path). Additional parameters are retrieved using the ordinal order in which they were passed, with ParamStr(1) being the first, and ParamStr(ParamCount) being the last. Each value received using ParamStr is a string value, and therefore has to be converted into the appropriate type before you can use it.
Here's a working example (pretty trivial, and all error checking omitted - you should, for instance, protect the code using StrToInt to handle errors if something is provided that won't convert to an integer).
program TestParams;
uses
SysUtils;
var
Input1, Input2, Output: Integer;
begin
if ParamCount > 1 then
begin
Input1 := StrToInt(ParamStr(1));
Input2 := StrToInt(ParamStr(2));
Output := Input1 + Input2;
WriteLn(Format('%d + %d = %d', [Input1, Input2, Output]));
end
else
begin
WriteLn('Syntax: ', ParamStr(0)); { Just to demonstrate ParamStr(0) }
WriteLn('There are two parameters required. You provided ', ParamCount);
end;
WriteLn('Press ENTER to exit...');
ReadLn;
end.
Calling it without parameters (or only one) displays the following:
C:\Temp>TestParams
Syntax: C:\Temp\TestParams.exe
There are two parameters required. You provided 0
Press ENTER to exit...
C:\Temp>TestParams 2
Syntax: C:\Temp>TestParams.exe 2
There are two parameters required. You provided 1
Press ENTER to exit...
Calling it with two parameters displays
C:\Temp\TestParams 2 2
2 + 2 = 4
Press ENTER to exit...
You need to understand the difference between a string and an integer.
To convert betwwen an integer such as 123 and the string of characters 1 2 3 you need to use a function. strtoint is one such function, converting a string to an integer. inttostr is another, converting from an integer to a string.
The command-line data is supplied via paramstr(n) as a string.
intvar := strtoint(paramstr(n));
assigns the value of the string to the integer variable intvar.
Whereas writeln has facilities to convert an integer argument to a formatted string, the way you have used it is attempting to output a string, so you need to convert the integer output to a string.
writeln(' output : ', inttostr(output) );
should do that very nicely.
var
x : string;
pcnt : integer;
begin
writeln('Parameter count=',inttostr(paramcount));
for pcnt := 1 to paramcount do
writeln('Parameter ',pcnt, ' of ',paramcount,' = ',paramstr(pcnt));
readln(x);
end.
should display the parameter list.
Indeed it is the case that the writeln procedure will recognise the variable type and take steps to format the value appropriately as a string, as has arrogantly been pointed out.
The issue to me is the difference between a string and an integer. paramstr returns a string which must be converted to an integer. After more than forty years' experience in Pascal, it's my opinion that it's better for a beginner to go through the exercise of converting each way and then use the conversion facility inbuilt in writeln.
First walk, then run. You first need to understand the steps in the procedure. Then you can start using the shortcuts - once you've mastered the basics.

how to get a variable out of a procedure in the right way in PASCAL?

I'm following an internet course on the basics of programming. After making a diagram I convert it to code, right now this is PASCAL language.
I'm having a problem with procedures and can't find an answer, nor in the course, nor with some google-ing.
I want to get a variavble back form a procedure. Right now iIhave a working piece of code but I think this is not the good way of working. Here's an extract of the code:
program WELKEWAGEN;
// declare your variables here
var T, N, KM, vari, prijsDW, prijsBW, jrenGEBR, taksDW, taksBW, prijsB, verbrBW, prijsD, verbrDW : real;
procedure OPHALEN(para : string);
begin
repeat
writeln('geef de ', para , ' op');
readln(vari);
until (vari > 0);
end;
begin
//this is the main program but there is more code ofcourse
OPHALEN('prijs benzinewagen');
prijsBW := vari;
//...
end.
Now the internet course says I should program it like this:
begin
//...
prijsBW := OPHALEN('prijs benzinewagen');
//...
end.
But this is not working.
I get following errors:
WELKEWAGEN.pas(24,14) Error: Incompatible types: got "untyped" expected "Real"
WELKEWAGEN.pas(50) Fatal: There were 1 errors compiling module, stopping
pas(24,14) is this line: prijsBW := OPHALEN('prijs benzinewagen');
Procedures don't return values, so the syntax
prijsBW := OPHALEN('prijs benzinewagen');
is invalid.
If you want to return a value, you need to define a function instead:
function OPHALEN(para : string): Real;
var
Res: Real;
begin
Res := 0;
repeat
writeln('geef de ', para , ' op');
readln(Res);
until (Res > 0);
OPHALEN := Res;
end;
Note that the (bad) global variables you're using mean you don't have to return anything at all, because a procedure can access and change that global variable directly (but you have no way of knowing when the procedure is finished):
procedure OPHALEN(para : string);
begin
vari := 0;
repeat
writeln('geef de ', para , ' op');
readln(vari);
until (vari > 0);
end;
Modern Pascal dialects (such as Delphi and FreePascal) allow a cleaner syntax for the return value of functions by using an automatically declared function result variable of the proper type for you, named Result (because that's what it is - the result of the function):
function OPHALEN(para : string): Real;
begin
Result := 0;
repeat
writeln('geef de ', para , ' op');
readln(Result);
until (Result > 0);
end;
If you need to return multiple values, you can use var parameters, which allow them to be changed inside the function.
procedure OPHALEN(para: string; var RetVal: Real);
begin
RetVal := 0;
repeat
writeln('geef de ', para , ' op');
readln(RetVal);
until (RetVal > 0);
end;
Your original code (and the examples I've provided above) all fail to allow the user to cancel, BTW. There should be some way to exit the loop for the user; otherwise, your code just endlessly loops, writing para to the screen and then waiting for input. This has a tendency to annoy users.

Resources