Pascal readkey seems to cause subseqent readln to ignore 1st character typed in response to readln. The readln only stores 2nd and later chars to var - pascal

I'm embarrassed to admit that I cannot understand why in this very simple example that the "readln(age);" statement in the "else if" clause ignores the first integer entered in response to the "writeln('How old are you ',name:length(name),'?');" statement around line 16.
It seems clear that the culprit is the "ch := readkey;" statement on line 18.
To enter a valid age, some key must first be pressed (other than (ASCII 13) which is trapped by the "if ch = #13 then" statement.
It does not seem to matter if the "age" variable is typed as a string or integer.
What happens is that if one enters two characters or integers, only the second is stored in the "age" variable. For example, to input an age of "25", I must first press some other key on the keyboard first, then enter "25" for "25" to be stored in the "age" variable.
Any help understanding this would be sincerely appreciated. Perhaps the entire approach is incorrect?
PROGRAM t5;
uses crt;
var
name,age : string;
ch : char;
procedure greet;
begin
clrscr;
writeln('What''s your name? ');
readln(name);
clrscr;
writeln('Hi, ',name:length(name));
writeln('How old are you ',name:length(name),'?');
ch := readkey;
if ch = #13 then
begin
writeln('Please enter a valid age!');
writeln ('You pressed the ','"','Enter','"',' key ','("',ord(ch),'")');
readln;
writeln('Bye!');
readln;
end
else if ch <> #13 then
begin
readln(age);
writeln('Gee, ',age,', - that''s really old!');
readln;
end;
end;
BEGIN
greet;
END.

ReadKey() reads one character (and removes it) from the keyboard buffer.
The problem is in this sequence:
ch := ReadKey; // the first digit is now in `ch`
if ch = #13 then // this condition is false
...
else if ch <> #13 then // this condition is true
begin
readln(age) // user types the second digit and <Enter>
// then you use age, but you have omitted the first character which is in `ch`
end;
Instead, use only ReadLn() and check that the returned characters are a proper number.

Related

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

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 .

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.

Pascal compiles and closes straight away despite readln;

program words;
uses crt;
type
T2DArray = array[1..100, 1..100] of string;
var
ch:char;
x,y:integer;
MapArray: T2DArray;
begin
x:=0;
y:=0;
repeat
MapArray[10, 10] := 'you are at a tree';
writeln(MapArray[x,y]);
write('current positon is ');
write(x);write(',');write(y);
ch:=ReadKey;
case ch of
#0 : begin
ch:=ReadKey; {Read ScanCode}
case ch of
'w' : y:=y+1;
'a' : x:=x-1;
's' : y:=y-1;
'd' : x:=x+1;
end;
end;
#27 : WriteLn('ESC');
end;
until ch=#27;
readln;
end.
i have this simple piece of code that will allow me to assign things to XY coordinates of a 2d array. the code compiles and closes straight away despite the readln; at the bottom.
All the best Arran.
Always enable range-checking {$R+} during development. You have a 1-based array but your x and y values are zero the first time you read from it.

Send BS (Backspace) to a TMemo

I would like to send a BackSpace control char to a TMemo like the user would actually press the BackSpace button.
My Memo is readonly and if I click a button it should delete the last char in the memo.
I would like to do that without using Memo.Text := ... (so no redraw or beginupdate, etc.)
Is that possible, if yes, how?
Thank you for your help.
EDIT: I tried to add #8 but no luck...
Sending a key press to a read only memo won't work. The key press will be ignored because the memo is read only. To delete the final character of a memo in an efficient way, that is without replacing the entire contents, you can use EM_SETSEL and EM_REPLACESEL.
var
Len: Integer;
begin
Len := Memo1.GetTextLen;
SendMessage(Memo1.Handle, EM_SETSEL, Len-1, Len);
SendMessage(Memo1.Handle, EM_REPLACESEL, 0, LPARAM(PChar('')));
end;
Or if you prefer a pure VCL version which wraps up these Windows messages:
begin
Memo1.SelStart := Memo1.GetTextLen-1;
Memo1.SelLength := 1;
Memo1.SelText := '';
end;
The latter probably sends a few more Windows messages, but is much easier to read. I would prefer the latter option.
One possible problem I can see with this is that it may not do what you want with line breaks. Since a Windows line break is two characters (CR+LF), you would need to delete two characters if the last character in the memo was LF. To handle that you can probably do it like this:
begin
Memo1.SelStart := Memo1.GetTextLen-1;
Memo1.SelLength := 1;
if Memo1.SelText=#10 then
begin
Memo1.SelStart := Memo1.SelStart-1;
Memo1.SelLength := 2;
end;
Memo1.SelText := '';
end;

Help in Pascal writing a word counter

I have to write a program in Pascal which has to detect how many words on a text (input by the user) start with a certain letter. I can't use arrays, can you give me any hints as to where to start?
If you know which letter, you merely need to keep a counter, no need for arrays.
If you don't know which letter, keep 26 counters. Stupid, but works as per your spec.
First thing to do is define the set of characters that constitute letters, or conversely which ones constitute non-letters.
Write a function that takes a character and returns a boolean based on whether that character is a letter. Then loop through the string and call it for each character. When you detect a letter right after a non-letter or at the start of the string, increment your counter if it is the target letter.
count instances of SPACE LETTER plus first word if it matches.
(S) is your input string;
Create a for loop that goes from 1 to the length of (S) - 1.
Inside loop, check is (S)[i] = ' ' and (S)[i+1] = 't' where i is the loop counter and 't' is the letter starting the word you want to count
If criteria in step two matches then increment a counter.
Note the minus one on the loop size.
Also, remember that the very first letter of the string may be the one you want to match and that will not get picked up by the loop defined above.
If you need to make your code smarter in that it can locate a specific letter rather than a hardcoded 't' then you can pass the requested character as a parameter to the function/procedure that your loop is in.
Off the top of my head - not tested
function WordCount(const S: string; const C: Char): Integer;
const
ValidChars: Set of Char [A..Z, a..z]; // Alter for appropriate language
var
i : Integer;
t : string;
begin
Result := 0;
if Length(S) <> 0 then
begin
t := Trim(S); // lose and leading and trailing spaces
t := t + ' '; // make sure a space is the last char
repeat
if (t[1] in ValidChars) and (t[1] = C then
inc(Result);
i := Pos(' ', t);
t := Copy(t(i+1, Length(t));
until Length(t) = 0;
end;
end;
Why would you need an array or a case statement?

Resources