read() strings of variable length - pascal

I've got rows of two values (input from console) that look likes this:
David 89000
Peter 99500
Jim 23999
END 1
is there a way to save the string and number into a variable other than to loop-read a char when you don't know the string length?
str:=''; salary:=0; i:=1;
while str<> 'END' do
begin
str:=''; salary:=0;
read(ch);
while ch <> ' ' do
begin
str:=str+ch;
read(ch);
end;
read(salary);
array[i].name:=str;
array[i].salary:=salary;
i:=i+1;
readln;
end;

You can do it with a single call to ReadLn and then parse the input yourself:
var
TextIn: string;
Person: string;
Salary: Integer;
begin
while true do
begin
ReadLn(TextIn); // Requires user to hit Enter
if Copy(TextIn, 1, 3) <> 'END' then
begin
Person := Copy(TextIn, 1, Pos(' ', TextIn) - 1);
Salary := StrToInt(Copy(TextIn, Pos(' ', TextIn) + 1, 255);
end
else
Exit;
end;
end;
I didn't include any error checking (which should be there), because your original code doesn't have any either.

Not with standard I/O functions. Of course you can put that code in a separate procedure, or split with tstringlist.

Related

Program Throwing Unknown Error: EConvertError

I am trying to create a program that takes a users input and checks if its valid as a date (years 2000 - 2099). when I was bug testing the code I put in an enter as the input twice in a row, the first time I entered an enter, no error was thrown but the second time the console flashes a message and crashes. I took a screenshot as it crashed and this is the error it showed
An unhandled exception occured at $0040CDE1
EConvertError : "" is an invalid integer
My best guess is that the error was caused by one of my StrToInt due to the EConvertError but it unclear to me as to why it only throws it on the second run through and not the first. I suspect that the error occurs on either line, 85, 98, 111, or 195.
When I initially wrote this program a was a beginner to Pascal so I apologise in advance for any sloppy code.
below is all of the code if you would like to run it for yourselves.
program DateVerifierAssignment;
uses crt, sysutils;
var
userInputArray: array[0..7] of string;
len, i, day, month, year: integer;
date, userInput, stringDay, stringMonth, stringYear: string;
errorCatch: boolean;
//DECLARING FUNCTIONS & PROCEDURES
procedure TitleScreen();
begin
//prints a coloured wall of text to give information like name and instructions
WriteLn(' o-------------------------o');
Write(' | ');TextColor(White);Write('Date Validation Program');TextColor(Cyan);WriteLn(' |');
WriteLn(' o-------------------------o');
WriteLn();
WriteLn(' o-------------------------o');
Write(' | ');TextColor(White);Write('Instructions');TextColor(Cyan);WriteLn(' |');
WriteLn(' o-------------------------o');
Write(' | ');TextColor(White);Write('Enter a date and the');TextColor(Cyan);WriteLn(' |');
Write(' | ');TextColor(White);Write('program will verify it');TextColor(Cyan);WriteLn(' |');
Write(' | ');TextColor(White);Write('and convert it to its');TextColor(Cyan);WriteLn(' |');
Write(' | ');TextColor(White);Write('long form');TextColor(Cyan);WriteLn(' |');
WriteLn(' o-------------------------o');
WriteLn();
TextColor(White);
Write(' press any key to begin');
//waits for user input then clears the screen and returns the text color to white
ReadKey();
ClrScr;
end;
function DateVerification(var userInput: string): boolean;
var
errorLimit : boolean;
bounds: integer;
begin
errorLimit := True;
//stores the length of the string as a variable
len := Length(userInput);
//checks to see if entry is 8 chracters long and displays an error message and returns user to input screen if it doesn't fit
if (len <> 8) and (errorLimit = True) then
begin
ClrScr();
TextColor(Red);
WriteLn('input was not the right length (8 characters)');
TextColor(White);
Write('make sure date fits format ');
TextColor(LightGreen);
WriteLn('dd/mm/yy');
TextColor(White);
Readkey();
ClrScr();
errorLimit := false;
Dateverification := false;
end;
//spits each character into its own spot in an array
for i := 1 to len do
userInputArray[i - 1] := userInput[i];
//tests if every slot in the array where a slash should be is a slash
for i := 0 to len-1 do
begin
if (userInputArray[2] <> '/') or (userInputArray[5] <> '/') and (errorLimit = true) then
begin
ClrScr();
TextColor(Red);
WriteLn('input did not have slashes in correct locations ');
TextColor(LightGreen);
WriteLn('dd/mm/yy');
TextColor(White);
Readkey();
ClrScr();
errorLimit := false;
Dateverification := false;
end;
end;
year := ((StrToInt(userInputArray[6]))*10) + StrToInt(userInputArray[7]);
if (year < 1) or (year > 99) and (errorLimit = true) then
begin
ClrScr();
TextColor(Red);
WriteLn('year was not from 0 to 99');
TextColor(White);
Readkey();
ClrScr();
errorLimit := false;
Dateverification := false;
end;
month := ((StrToInt(userInputArray[3]))*10) + StrToInt(userInputArray[4]);
if (month < 1) or (month > 12) and (errorLimit = true) then
begin
ClrScr();
TextColor(Red);
WriteLn('month was not from 1 to 12');
TextColor(White);
Readkey();
ClrScr();
errorLimit := false;
Dateverification := false;
end;
day := ((StrToInt(userInputArray[0]))*10) + StrToInt(userInputArray[1]);
if (month = 4) or (month = 6) or (month = 9) or (month = 11) then
bounds := 30;
if (month = 2) then
begin
if (IsLeapYear(year) = true) then
bounds := 29
else
bounds := 28;
end
else
bounds := 31;
if (day < 1) or (day > bounds) and (errorLimit = true) then
begin
ClrScr();
TextColor(Red);
WriteLn('day was not from 1 to days in month');
TextColor(White);
Readkey();
ClrScr();
errorLimit := false;
Dateverification := false;
end;
if (errorLimit = true) then
DateVerification := True;
end;
function IsLeapYear(var year: integer): boolean;
//simple function to determine if a year is a leap year on the gregorian calender
begin
if (year mod 4) = 0 then
if (year mod 100 <> 0) then
if (year mod 400 = 0) then
IsLeapYear := true
else
IsLeapYear := false
else
IsLeapyear := false
else
IsLeapyear := false
end;
procedure DateToAlpha(var userInput: string);
begin
end;
//MAIN PROGRAM
begin
//preparing for while loop later in code and changes text colour to cyan
errorCatch := true;
TextColor(Cyan);
//Displays a title screen and instruction about how to use(stored in a procedure to help with readability)
TitleScreen();
//begins a loop so that if an error is to occur the program can easily ask for a new date and try again
while (errorCatch = true) do
begin
//sets error catch to 0 so that if there a no errors the program will not loop
errorCatch := false;
//displays information on how to input a date as well as an example with different colours for better readability
Write('Enter a date in the format');TextColor(LightGreen); WriteLn(' dd/mm/yy');TextColor(White);
Write('e.g. ');TextColor(LightGreen);WriteLn(' 09/07/20'#13#10);TextColor(White);
//takes date user inputs and stores it
ReadLn(userInput);
//calls the date verification function to see if the date is valid
//(refer to dateVerification at the top of the program for more info and comments)
if (DateVerification(userInput) = false) then
errorCatch := true;
len := Length(userInput);
for i := 1 to len do
userInputArray[i - 1] := userInput[i];
year := ((StrToInt(userInputArray[6]))*10) + StrToInt(userInputArray[7]);
readKey();
end;
end.
Ordinarily on SO we don't provide fully-coded answers to qs which are obviously coursework.
In this case I thought I'd make an exception because it was obvious that you are making
unnecessarily heavy weather of this, and the way you've coded it is likely to actually get in the way
of debugging it and getting it working correctly.
So, the example below shows a very simple,
clean way to validate a date in the 21st century supplied in dd/mm/yyyy format. It
shows the correct order of processing and validation steps; if it detects an error
in any step, it says what the problem is and halts. If execution drops through to the end,
the supplied date string must be valid.
I have deliberately left out any loops
or prettifying code, because the important thing to get right first is a clean and concise
coding of the processing and validation steps. If you want to use loops, fine, but the way you've used
e.g. errorCatch, I bet that if you come back to your code in 6 months, you won't remember how
it's supposed to work. You don't need any complicated flags or loops for this task - the user simply has to be able to type exactly eight characters (followed by [Enter]) and the code will tell then whether it's valid or not.
Note I've used the standard Copy function to separate out the various parts of the
date inputted. Btw, your declaration
userInputArray: array[0..7] of string;
is just plain wrong. If you want an array of a certain number of characters, fine, but that would
just needlessly complicate handling and processing what the user types. It's far
simpler, and therefore less error-prone to accept a single string of 8 characters
and just deal with that. But in any case, to permit the string to include the / characters after the day and month digits, the length of the stringg should be 10, not eight, to allow for those and four year digits.
program DateVerification;
uses
SysUtils;
var
sUserInput,
sDay,
sMonth,
sYear : String;
iDay,
iMonth,
iYear,
iDays: Integer;
bIsLeapYear : Boolean;
function DaysInMonth(iMonth, iYear : Integer) : Integer;
begin
Result := -1; // you supply the code for this taking into account leap year for February
end;
begin
writeln('Please enter a date between 01/01/2000 and 31/12/2099 in the format shown');
readln(sUserInput);
if Length(sUserInput) <> 10 then begin
writeln('Input is wrong length.');
Halt;
end;
if (sUserInput[3] <> '/') or (sUserInput[6] <> '/') then begin
writeln('Input is incorrectly delimited.');
Halt;
end;
sDay := Copy(sUserInput, 1, 2);
sMonth := Copy(sUserInput, 4, 2);
sYear := Copy(sUserInput, 7, 4);
iYear := StrToInt(sYear);
if (iYear < 2000) or (iYear > 2099) then begin
writeln('Invalid year : ', sYear);
Halt;
end;
bIsLeapYear := IsLeapYear(iYear); // you supply the code for IsLeapYear
iMonth := StrToInt(sMonth);
if (iMonth < 1) or (iMonth > 12) then begin
writeln('Invalid month : ', sMonth);
Halt;
end;
iDay := StrToInt(sDay);
if (iDay < 1) or (iDay > DaysInMonth(iMonth, iYear)) then begin
// You need to supply the DaysInMoth function, which of course
// needs to account for February in a leap year
writeln('Invalid day of month: ', sMonth);
Halt;
end;
writeln(sUserInput, ' is a valid date.');
readln;
end.

Lazarus function to find 8 digit numbers in a string

I have e-mail subject lines and I want to find ticket references in them it could be the TT ref is like 12345678. One subject line (string) can have multiple 8 digit numbers!
I have been using the below code but it is merely stripping out the first 8 digits then doing a check if that is 8 char long:
function StripNumbers(const aString: string): string;
var
C: char;
begin
Result := '';
for C in aString do
begin
if CharInSet(C, ['0'..'9']) then
begin
Result := Result + C;
end;
end;
end;
Example:
my string variable is
subject := "yada yada XF12345678 blabla XF87654321 duh XF11.223344"
function GetTTRefs(subject) should result "12345678;87654321;"
Thank you for answers.
function GotTTRefs(Subject:string;Digits:Byte):string;
var
i:integer;
TT:string;
begin
i:=1;
while i <= Length(Subject)-Digits+1 do
begin
if Subject[i] in ['1'..'9'] then
begin
TT:=Copy(Subject,i,Digits);
if (StrToQWordDef(TT, 0) <> 0) then
Result:=Result+TT+';';
end;
inc(i);
end;
end;

Checking if word is palindrome with function

I have to write a program in Pascal which checks whether a word is a palindrome.
For example:
if I input "abba" then write 'TRUE'
input 'abb a' then write 'TRUE'
input 'abca' write 'FALSE'
I wrote this:
program palindromek;
var i,j,delka,pul:integer;
str:string;
function palindrom(slovo:string):boolean;
const mezera=32;
begin
delka:=length(str);
if (delka mod 2) = 0 then pul:=delka div 2
else pul:=(delka-1) div 2;
for i:=1 to delka do
begin
if (ord(slovo[i])>=ord('a')) and (ord(slovo[i])<=ord('z')) then
begin
if (delka>=4)and(delka<=100) then
begin
if (length(str) mod 2) = 0 then {slovo se sudym poctem pismen}
begin
for j:=1 to pul do
begin
if slovo[j]=slovo[length(str)-j+1]
then palindrom:=true else palindrom:=false
end
end else
begin
for j:=1 to pul do
begin
if slovo[j]=slovo[length(str)-j+1]
then palindrom:=true else palindrom:=false
end
end
end else if slovo[1]=slovo[delka]
then palindrom:=true else palindrom:=false
end
end;
end;
begin
readln(str);
writeln(palindrom(str));
end.
but it has to ignore spaces. Do you have any idea please?
To remove all spaces, you can use function like this:
procedure RemoveSpacesInplace(var s: string);
var
i, SpaceCount: Integer;
begin
SpaceCount := 0;
for i := 1 to Length(s) do
if s[i] = ' ' then
Inc(SpaceCount)
else
s[i - SpaceCount] := s[i];
SetLength(s, Length(s) - SpaceCount);
end;
You can modify it for other non-letter chars.
Note that your logic for odd and even length is excessive. Try to simplify it.
You can use the functions StringReplace and ReverseString for your task.
program palindromek;
uses SysUtils, StrUtils;
var
str:string;
function palindrom(slovo:string):boolean;
begin
slovo := StringReplace(slovo, ' ', '', [rfReplaceAll]);
Result := slovo = ReverseString(slovo)
end;
begin
readln(str);
writeln(palindrom(str));
readln;
end.
If you are not allowed to use SysUtils and StrUtils then you can manually reverse your string and then compare if the original string and the reversed string are equal.
This would look something like this: (not tested!)
function palindrom(slovo:string):boolean;
var slovofor: string;
slovorev: string;
i: integer;
begin
for i:= length(slovo) downto 1 do begin
if slovo[i] <> ' ' then begin
slovofor := slovofor + slovo[length(slovo)-i+1];
slovorev := slovorev + slovo[i];
end;
end;
writeln(slovofor);
Result := slovofor = slovorev
end;

Why does my program not output all my data?

program ZZX1;
{$mode objfpc}{$H+}
uses
crt,
wincrt,
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes
{ you can add units after this };
type
Masquerader = record
Name, CountyCode: string;
Payment: real;
end;
var
Applicant: array[1..10] of Masquerader;
DemList: array[1..10] of string;
BerList: array[1..10] of string;
EsqList: array[1..10] of string;
x:integer;
Y:integer;
DemCounter:integer;
BerCounter:integer;
EsqCounter:integer;
DemAmount:real;
BerAmount:real;
EsqAmount:real;
procedure LoadData;
begin
clrscr;
X:=0;
DemCounter:=0;
BerCounter:=0;
EsqCounter:=0;
DemAmount:=0;
BerAmount:=0;
EsqAmount:=0;
repeat
X:= x+1;
repeat
write('Enter Your County Code DemM or BerM or EsqM: ');
readln(Applicant[x].CountyCode);
until (Applicant[x].CountyCode= 'DemM') or (Applicant[x].CountyCode= 'BerM') or (Applicant[x].CountyCode= 'EsqM');
If Applicant[x].CountyCode = 'DemM' then
begin
write('Enter Your Name: ');
readln(Applicant[x].Name);
write('Enter Your Total Payment: ');
readln(Applicant[x].Payment);
clrscr;
DemCounter:= DemCounter + 1;
DemAmount:= DemAmount + Applicant[x].Payment;
DemList[DemCounter]:= Applicant[x].Name;
end;
If Applicant[x].CountyCode = 'BerM' then
begin
write('Enter Your Name: ');
readln(Applicant[x].Name);
write('Enter Your Total Payment: ');
readln(Applicant[x].Payment);
clrscr;
BerCounter:= BerCounter + 1;
BerAmount:= BerAmount + Applicant[x].Payment;
BerList[BerCounter]:= Applicant[x].Name;
end;
If Applicant[x].CountyCode = 'EsqM' then
begin
write('Enter Your Name: ');
readln(Applicant[x].Name);
write('Enter Your Total Payment: ');
readln(Applicant[x].Payment);
clrscr;
EsqCounter:= EsqCounter + 1;
EsqAmount:= EsqAmount + Applicant[x].Payment;
EsqList[EsqCounter]:= Applicant[x].Name;
end;
until x=6 ;
end;
Procedure PrintData;
begin
Y:= 0;
for y := 1 to 6 do
begin
writeln('Name: ', Applicant[y].Name);
writeln('CountyCode: ', Applicant[y].CountyCode);
writeln('Payment: ', Applicant[y].Payment:0:2);
writeln;
end;
For Y:= 1 to DemCounter do
begin
writeln(DemList[Y]);
writeln(DemCounter,'',' persons are registered in Demerara');
writeln;
writeln('DemTotal:$ ', DemAmount:0:2);
end;
For Y:= 1 to BerCounter do
begin
writeln(BerList[Y]);
writeln(BerCounter,'',' persons are registered in Berbice');
writeln;
writeln('BerTotal:$ ', BerAmount:0:2);
end;
For Y:= 1 to EsqCounter do
begin
writeln(EsqList[Y]);
writeln(EsqCounter,'',' persons are registered in Essequibo');
writeln;
writeln('EsqTotal:$ ', EsqAmount:0:2);
end;
end;
Procedure quit;
begin
writeln('Press <Enter> To Quit');
readln;
end;
begin
LoadData;
PrintData;
quit;
end.
This program currently collects 6 persons and groups them by their countycode, calculating the total amount of persons and money collected by each county.
When I run the program below my expected output is on the screen for a few seconds then it disappears leaving only a piece of the expected output( The end Part). Please assist.
If there are characters in the keyboard buffer when the program reaches the readln; statement in the procedure quit, readln will read those characters and continue onwards rather than waiting for further input before continuing.
To check this, try adding a character variable as a parameter to readln and write the ASCII value of the character out (or check its value in a debugger) to see if there is anything in that variable after the readln.
(EDIT)
After further thinking, I wonder if the code like:
For Y:= 1 to EsqCounter do
begin
writeln(EsqList[Y]);
writeln(EsqCounter,'',' persons are registered in Essequibo');
writeln;
writeln('EsqTotal:$ ', EsqAmount:0:2);
end;
... should actually read something like:
For Y:= 1 to EsqCounter do
begin
writeln(EsqList[Y]);
end;
writeln(EsqCounter,'',' persons are registered in Essequibo');
writeln;
writeln('EsqTotal:$ ', EsqAmount:0:2);
... because otherwise the same values of EsqCounter and EsqTotal will be output EsqCounter times, which seems unnecessary.

How does this program to count vowels work?

I want to understand this code, especially PROCEDURE
PROGRAM vowels;
USES crt;
{Program that counts the number of vowels in a sentence}
CONST space=' ';
maxchar=80;
TYPE vowel=(a,e,i,o,u);
VAR buffer:ARRAY[1..maxchar] of char;
vowelcount:ARRAY[vowel] of integer;
PROCEDURE initialize;
VAR ch:vowel;
BEGIN
FOR ch:=a TO u DO
BEGIN
vowelcount[ch]:=0;
END;
END;
PROCEDURE textinput;
VAR index:integer;
BEGIN
writeln('Input a sentence');
FOR index:=1 TO maxchar DO
IF eoln THEN buffer[index]:=space
ELSE read(buffer[index]);
readln;
END;
PROCEDURE analysis;
VAR index:integer;
ch:vowel;
BEGIN
index:=1;
WHILE index<>maxchar+1 DO
BEGIN
IF buffer[index] IN ['a','e','i','o','u'] THEN
BEGIN
CASE buffer[index] OF
'a':ch:=a;
'e':ch:=e;
'i':ch:=i;
'o':ch:=o;
'u':ch:=u;
END;
vowelcount[ch]:=vowelcount[ch]+1;
END;
index:=index+1;
END;
END;
PROCEDURE vowelout;
VAR ch:vowel;
BEGIN
clrscr;
writeln;
writeln(' a e i o u');
FOR ch:=a TO u DO
write(vowelcount[ch]:4);
writeln;
END;
BEGIN
initialize;
textinput;
analysis;
vowelout;
END;
Overall: Okay this code is counting the number of vowels supplied in the input string.
Lets Begin....
TYPE vowel=(a,e,i,o,u); VAR
buffer:ARRAY[1..maxchar] of char;
vowelcount:ARRAY[vowel] of integer;
This code is defining a list of the vowels in english (a,e,i,o,u).
PROCEDURE initialize; VAR ch:vowel;
BEGIN FOR ch:=a TO u DO BEGIN
vowelcount[ch]:=0; END; END;
It then defines a variable to collect the number of each vowel, called vowelcount. That variable is an array, looks sort of like this:
vowelcount[a]=0;
vowelcount[e]=0;
vowelcount[i]=0; #... etc
Then the procedure "Analysis" is defined. This takes the input from the screen (which will be called later on in the program) and steps through each letter in the input.
WHILE index<>maxchar+1 DO BEGIN IF
buffer[index] IN ['a','e','i','o','u']
THEN BEGIN CASE buffer[index] OF
'a':ch:=a; 'e':ch:=e; 'i':ch:=i;
'o':ch:=o; 'u':ch:=u; END;
If any of those letters happens to be in the list of letters than matches a vowel, then it will add one to the number in the vowelcount array above. (vowelcount[ch]:=vowelcount[ch]+1) where ch is the matched letter. As you can see this is only triggered if it is a valid vowel (IF buffer[index] IN ['a','e','i','o','u'] )
Finally. The main code of the program, or what is actually run:
BEGIN clrscr; writeln; writeln(' a e i
o u'); FOR ch:=a TO u DO
write(vowelcount[ch]:4); writeln; END;
BEGIN initialize; textinput; analysis;
vowelout; END.
This basically strings the application together, starting by clearing the screen (in a dos prompt) and then outputting the vowels onto the screen. It then adds some formatting and outputs the current count of vowelcount (as above).
It will then request your input and finally it will output the contents of vowelcount again, which has been updated with the vowelcounts from the input you made.

Resources