Reading A Variable As Both A Number And String of Words - performance

I am trying to get Dep_Code to read as a string after choosing the options given (1, 2 or 3). I first had it set to integer in my first program (I think) and was able to get it to read out the options given as words (Accounts ACC or the others). However, it was accidentally deleted. I've tried various ways to get it even setting Dep_Code as a string but its not working and I keep getting a variety of errors. Btw, I'm not familiar with programming so I'm aware that the following code is quite incorrect... but I hope you all can help. Thank you!
REPEAT
writeln ('Please enter the Department Code:- ');
writeln;
writeln ('1. Accounts (ACC)');
writeln ('2. Human Resources (HR)');
writeln ('3. Operations (OP)');
writeln;
readln (Dep_Code);
IF Dep_Code = 1 THEN
Dep_Code := ('Accounts (ACC)')
ELSE IF Dep_Code = 2 THEN
Dep_Code := ('Human Resources(HR)')
ELSE IF Dep_Code = 3 THEN
Dep_Code := ('Operations (OP)');
UNTIL ((Dep_Code >= 1) AND (Dep_Code <= 3));

This is impossible. Pascal is a strictly typed language, and something cannot be an Integer and a string at the same time, and variables cannot change type either:
IF Dep_Code = 1 THEN
Dep_Code := ('Accounts (ACC)')
But you don't need a string at all. Keep it an integer. The functions that handle the various depts can write or define such strings, if necessary. Your logic for the menu does not need a string variable.
Do something like:
procedure HandleAccounts(var Error: Boolean);
begin
...
end;
// Skipped the other functions to keep this answer short ...
var
Dep_Code: Integer;
AllFine: Boolean;
// Skip the rest of the necessary code ...
repeat
// Skipped the Writelns to keep this answer short ...
Readln(Dep_Code);
Error := False;
case Dep_Code of
1: HandleAccounts(Error);
2: HandleHumanResources(Error);
3: HandleOperations(Error);
else
Error := True;
end;
until not Error;
Above, I skipped some of the code. You can fill in the blanks, I guess.

Related

get multiple inputs instead of one

So, I'm new here and I'm new to programming generally. I made this program that I needed for a project (a pascal program using Lazarus) that allows me to get a kind of list generated by replacing * by numbers. What I need is to be able to give it multiple codes to process at once (maximum 10) instead of entering every code at once.
program b;
{$mode objfpc}
{$H+}
uses sysutils;
var
sourcestr: string;
resultstr: string;
n: integer;
begin
writeln('provide a string:');
readln(sourcestr);
for n := 0 to 99 do begin
resultstr := StringReplace(sourcestr, '*', IntToStr(n div 10), []);
resultstr := StringReplace(resultstr, '*', IntToStr(n mod 10), []);
resultStr := resultStr + ':password';
writeln(resultstr);
end;
end.
I hope you could help me with this and thanks in advance.
The code below shows how to replace an arbitrary number of pairs of asterisks by the two substitute characters you are generating.
for n := 0 to 9 do begin
resultstr := sourcestr;
while Pos('*', resultstr) > 0 do begin
stringReplace(resultstr, '*', IntToStr(n div 10), []);
resultstr := StringReplace(resultstr, '*', IntToStr(n mod 10), []);
end;
resultStr := resultStr + ':password';
writeln(resultstr);
end;
It uses the Pos function in a while loop to replace the asterisk pairs. Be aware that the output may not be exactly what you need, because in each generated resultstr you will get the same substitute characters replacing each pair of asterisks, i.e.
with an input of
a ** b ** c
the resultstrs generated will be like
a00b00c
a11b11c
which may not be what you need. If not, changing the code to do what you do need is left as an exercise for the reader, as they say.
Btw, it occurred to me later that maybe you are asking how to input and process several lines'-worth of user input. One way to do that would be to read the lines into a TStringList (see online help) and then process that. Something like:
var
TL : TStringList;
sourcestr : String;
begin
TL := TStringList.Create;
repeat
readln(sourcestr);
if sourcestr <> '' then
TL.Add(sourcestr);
until sourcestr = '';
for i := 0 to TL.Count - 1 do begin
sourcestr := TL[i];
// process sourcestr however you want
end;
TL.Free;
though you could, of course, simply process sourcestr as you go along, in the repeat..until loop.

Binary file error in Lazarus Pascal with custom records - error SIGSEGV

I don't work with Pascal very often so I apologise if this question is basic. I am working on a binary file program that writes an array of custom made records to a binary file.
Eventually I want it to be able to write multiple arrays of different custom record types to one single binary file.
For that reason I thought I would write an integer first being the number of bytes that the next array will be in total. Then I write the array itself. I can then read the first integer type block - to tell me the size of the next blocks to read in directly to an array.
For example - when writing the binary file I would do something like this:
assignfile(f,MasterFileName);
{$I-}
reset(f,1);
{$I+}
n := IOResult;
if n<> 0 then
begin
{$I-}
rewrite(f);
{$I+}
end;
n:= IOResult;
If n <> 0 then
begin
writeln('Error creating file: ', n);
end
else
begin
SetLength(MyArray, 2);
MyArray[0].ID := 101;
MyArray[0].Att1 := 'Hi';
MyArray[0].Att2 := 'MyArray 0 - Att2';
MyArray[0].Value := 1;
MyArray[1].ID := 102;
MyArray[1].Att1:= 'Hi again';
MyArray[1].Att2:= MyArray 1 - Att2';
MyArray[1].Value:= 5;
SizeOfArray := sizeOf(MyArray);
writeln('Size of character array: ', SizeOfArray);
writeln('Size of integer var: ', sizeof(SizeOfArray));
blockwrite(f,sizeOfArray,sizeof(SizeOfArray),actual);
blockwrite(f,MyArray,SizeOfArray,actual);
Close(f);
Then you could re-read the file with something like this:
Assign(f, MasterFileName);
Reset(f,1);
blockread(f,SizeOfArray,sizeof(SizeOfArray),actual);
blockread(f,MyArray,SizeOfArray,actual);
Close(f);
This has the idea that after these blocks have been read that you can then have a new integer recorded and a new array then saved etc.
It reads the integer parts of the records in but nothing for the strings. The record would be something like this:
TMyType = record
ID : Integer;
att1 : string;
att2 : String;
Value : Integer;
end;
Any help gratefully received!!
TMyType = record
ID : Integer;
att1 : string; // <- your problem
That field att1 declared as string that way means that the record contains a pointer to the actual string data (att1 is really a pointer). The compiler manages this pointer and the memory for the associated data, and the string can be any (reasonable) length.
A quick fix for you would be to declare att1 something like string[64], for example: a string which can be at maximum 64 chars long. That would eliminate the pointer and use the memory of the record (the att1 field itself, which now is a special static array) as buffer for string characters. Declaring the maximum length of the string, of course, can be slightly dangerous: if you try to assign the string a string too long, it will be truncated.
To be really complete: it depends on the compiler; some have a switch to make your declaration "string" usable, making it an alias for "string[255]". This is not the default though. Consider also that using string[...] is faster and wastes memory.
You have a few mistakes.
MyArray is a dynamic array, a reference type (a pointer), so SizeOf(MyArray) is the size of a pointer, not the size of the array. To get the length of the array, use Length(MyArray).
But the bigger problem is saving long strings (AnsiStrings -- the usual type to which string maps --, WideStrings, UnicodeStrings). These are reference types too, so you can't just save them together with the record. You will have to save the parts of the record one by one, and for strings, you will have to use a function like:
procedure SaveStr(var F: File; const S: AnsiString);
var
Actual: Integer;
Len: Integer;
begin
Len := Length(S);
BlockWrite(F, Len, SizeOf(Len), Actual);
if Len > 0 then
begin
BlockWrite(F, S[1], Len * SizeOf(AnsiChar), Actual);
end;
end;
Of course you should normally check Actual and do appropriate error handling, but I left that out, for simplicity.
Reading back is similar: first read the length, then use SetLength to set the string to that size and then read the rest.
So now you do something like:
Len := Length(MyArray);
BlockWrite(F, Len, SizeOf(Len), Actual);
for I := Low(MyArray) to High(MyArray) do
begin
BlockWrite(F, MyArray[I].ID, SizeOf(Integer), Actual);
SaveStr(F, MyArray[I].att1);
SaveStr(F, MyArray[I].att2);
BlockWrite(F, MyArray[I].Value, SizeOf(Integer), Actual);
end;
// etc...
Note that I can't currently test the code, so it may have some little errors. I'll try this later on, when I have access to a compiler, if that is necessary.
Update
As Marco van de Voort commented, you may have to do:
rewrite(f, 1);
instead of a simple
rewrite(f);
But as I replied to him, if you can, use streams. They are easier to use (IMO) and provide a more consistent interface, no matter to what exactly you try to write or read. There are streams for many different kinds of I/O, and all derive from (and are thus compatible with) the same basic abstract TStream class.

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.

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.

Reading record from a text file into array in Pascal

1.this is my code i want to read a record from a text file into array in pascal my program is about making a hotel helper and i already have a text file with the data of the hotel then i should read it from the text file and store it in array .. but i am facing error 103 exit code (file not open).... any help Please . :)
program Hotel1(input,output);
const max =10; MaxFloor =10;
type
Date = record
day :1..31;
month:1..12;
year:integer;
end;
Booking = record
Guest:string[20];
S_Date:date;
E_date:date;
end;
Booking_Mat= array[1..max] of Booking;
History_Booking = record
B_num:integer;
B_Mat:Booking_Mat;
end;
Room = record
Num:integer;
Bed_num:integer;
Price:integer;
Status:Boolean;
H:History_Booking;
end;
Data = record
Ro:Room;
m:integer;
end;
Data_mat= array [1..max] of Data;
Procedure Read_Data(filename:string; var table:Data_mat);
var df:text; i,j :integer;
n,m,num,GN:integer;
Bed_num,Price:integer;
f:text;
s,e:Date;
Gname:string[20];
ok:boolean;
a:Data_mat;
c:char;
Begin
writeln('Reading ',filename,' records into array.... ');
assign(df,filename);
reset(df);
i:=0;
while (not eof) do
begin
i:=i+1;
Read (f,num);
a[i].Ro.num:=num;
Read (f,Bed_num);
a[i].Ro.Bed_num:=Bed_num;
Read (f,Price);
a[i].Ro.Price:=Price;
Read(f,c);
if (c ='Y') then
a[i].Ro.status:= true
else
a[i].Ro.status:= false;
readln;
End; {while eof}
close(df);
End; {Read_Data}
You've declared two variables of type Text, (df and f) in your var block.
You open df with these lines:
assign(df,filename);
reset(df);
You then read from f (which is not the file you opened above) in several lines, such as this one:
Read (f, num);
It's interesting to note that you actually manage to close the file you really opened, even though you never use it in your loop:
close(df);
The solution to all of these issues is to delete the declaration of either f or df, and then fix the compiler errors you get by correcting the code to use the remaining text variable. (Two important lessons here are
Only declare the variables you actually need.
Use the variables you declare.
Your loop is also invalid, because you're using while not eof with no file provided for which to test the end. Your loop should read while not Eof(df) do instead.
It's also much better to follow the typical naming convention of prefixing types with a T. It makes it clear that it's a type and not a variable, and allows you to read the code more easily. For instance, I'd change your definition of Data to TRoomData, and change the other type declarations accordingly. Here's an example - note that TRoomData now has a field (member) named Room of type TRoom:
TRoomData = record
Room: TRoom;
m: Integer;
end;
TRoom is defined as
TRoom = record
Num: Integer;
Bed_num: Integer;
Price: Integer;
Status: Boolean;
H: THistory_Booking;
end;
And so forth. This allows you to write code more clearly:
var
RoomData: TRoomData;
begin
RoomData.Room.Num := 1;
RoomData.Room.Price := 50;
// etc.
end;
With all that being said, your file does not contain text, and therefore you're using the wrong file type by using df: Text in the first place. You should use a File of TRoomData, allowing you to read and write entire records at a time. Here's an example of doing so:
var
DF: File of TRoomData;
RoomData: TRoomData;
i: Integer;
const
DataFileName = 'D:\TempFiles\RoomData.dat';
Writing it:
// Put some data into the record
RoomData.Room.Num := 1;
RoomData.Room.Bed_num := 1;
RoomData.Room.Price := 40;
RoomData.Room.Status := True;
RoomData.Room.H.B_num := 1;
for i := 1 to Max do
begin
RoomData.Room.H.B_Mat[1].Guest := Format('Guest %d', [i]);
RoomData.Room.H.B_Mat[1].S_Date.Year := 2014;
RoomData.Ro.H.B_Mat[1].S_Date.Month := i;
RoomData.Ro.H.B_Mat[1].S_Date.Day := i;
end;
// Write it out to the file
AssignFile(DF, DataFileName);
try
Rewrite(DF);
Write(DF, RoomData);
finally
CloseFile(DF);
end;
Reading it back in:
AssignFile(DF, DataFileName);
try
Reset(DF);
Read(DF, RoomData);
finally
CloseFile(DF);
end;
(Or, better yet: If the version of Pascal you're using supports it, move away from the old file I/O routines and start using TFileStream instead.)
Last but not least, learn to properly format your code. It makes it much easier to debug and maintain, and it's much easier to read when you can follow the execution path clearly.

Resources