My school assignment was to create a menu-like program with pascal that shows the code of other programs. It reads the text from a file line by line and prints it to the console. It works just fine to the point when it doesnt show the full output. This is the output that it shows:
http://postimg.org/image/3r4ua8poz/
When it runs it shows everything line by line, but at the end you can only see the part of the code that fits in the console window and you cant scroll up. Can anyone tell me how to fix this? It will be much appresiated.
Here is the actuall code:
program pascalMenu //ime na programata
{$mode objfpc};//OOP pascal ili neshto takova(zaduljitelno e)
uses sysutils,
crt, graph;
Var choice : array[1..5] of string;//masiv, v sluchaq sadurja chas ot imenata na failovete
programFile : Text;//shte sudurja imeto na faila
userFile,line,number,filename : string;
i,j,value : integer;
c : char;
begin
writeln('This program will open all of our previous tasks.');
//pulnene na masiva
for i := 1 to 5 do
begin
str(i,number);
choice[i] := 'program' + number;
end;
j:=1;
repeat
writeln( 'Use the arrow keys to navigate, ENTER to open and ESC to close.');
//pokazvane na elementite v masiva sled vsqka vrutka na cikula
for i:= 1 to 5 do
begin
if i = j then
begin
//tva e za cveta
textcolor(red);
writeln(choice[i]);
textcolor(white);
continue;
end;
writeln(choice[i]);
end;
//beggining the menu loop
c := readkey;//chetene na kopche rofl
clrscr;// - chisti konzolata(optional)
if ord(c) = 0 then
c := readkey;
value := ord(c);
//options
case value of
72 :
begin
j:= j - 1;
if j < 1 then j :=5;
end;
80:
begin
j:= j + 1;
if j > 5 then j:= 1;
end;
13:
begin
str(j,number);
filename := 'program' + number + '.txt';
assignfile(programFile,filename);//prilaga imeto na faila na promenlivata
//tva nz za kakvo e, obache sled 5-q opit reshi, che bez nego nemoje
try
reset(programFile);//otvarq faila za chetene
repeat
readln(programFile, line);//chete 1 red ot faila v line
writeln(line);
until (Eof(programFile));//eof - end of file
closefile(programFile);//zaduljitelno!! bez nego ne mogat dase otvarqt 2/poveche faila
//zaradi greshka v read/write dostupa
except on E:EInOutError do begin
writeln('The program could not read the file. Check the file''''s''name and directory.');end;
end;
writeln('Press any key to return to the main menu.');
writeln('P.S. - This will erase the text');
readln;
clrscr;
end;
end;
until value =27;
end.
The scrolling is prohibited by the use of unit Crt. Unuse it.
Or have a look at the "lister" demo that comes with FPC, which tries a bit of the same (cursor movement for selection and displaying)
Related
I am trying to build an REPL in Ada after not having touched the language for many years.
If I check for END_OF_FILE in the code, the way that Text_IO buffers input and output changes. I remember this being the case but cannot find a description of why or how to deal with this. (I suspect this is a difference between terminal IO and file based IO)
Can anyone point me to a description of why Text_IO behaves this way?
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Exceptions; use Ada.Exceptions;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure fred is
Buffer: Unbounded_String := Null_Unbounded_String;
L : integer := 0;
begin
-- while (not End_Of_File)
loop
L := L+1;
put("--> ");
Buffer := To_Unbounded_String(Get_Line);
Put_Line(Integer'image(L) & " " & to_String(Buffer));
end loop;
exception
when END_ERROR =>
put_line("");
put_line("Bye ...");
end fred;
Running this code as-is gives
--> a
1 a
--> s
2 s
--> d
3 d
-->
Bye ...
If I uncomment the while statement I get
a
--> 1 a
s
--> 2 s
d
--> 3 d
Since End_Of_File requires user input, just place it right where you read the user input:
loop
L := L + 1;
Put ("--> ");
exit when End_Of_File;
Buffer := To_Unbounded_String (Get_Line);
Put_Line (Integer'Image(L) & " " & To_String (Buffer));
end loop;
As it's said in the title Im having trouble finding a solution on how to check if a string PW contains a number or not. How can i check in TP if the string PW contains a digit?
repeat
writeln;
writeln('Ok, please enter your future password.');
writeln('Attention: The Text can only be decoded with the same PW');
readln(PW);
pwLength:= Length(PW);
error:=0;
for i:= 1 to Length(PW) do begin
if Input[i] in ['0'..'9'] then begin
error:=1;
end;
end;
if Length(PW)=0 then
begin
error:=1;
end;
if Length(PW)>25 then
begin
error:=1;
end;
if error=1 then
begin
writeln('ERROR: Your PW has to contain at least 1character, no numbers and has to be under 25characters long.');
readln;
clrscr;
end;
until error=0;
This is how I would write your code:
var
PW : String;
Error : Integer;
const
PWIsOk = 0;
PWIsBlank = 1;
PWTooLong = 2;
PWContainsDigit = 3;
procedure CheckPassword;
var
i : Integer;
begin
writeln;
writeln('Ok, please enter your future password.');
writeln('Attention: The Text can only be decoded with the same PW');
writeln('Your password must be between 1 and 25 characters long and contain no digits.');
repeat
error := PWIsOk;
readln(PW);
if Length(PW) = 0 then
Error := PWIsBlank;
if Error = PWIsOk then begin
if Length(PW) > 25 then
Error := PWTooLong;
if Error = 0 then begin
for i := 1 to Length(PW) do begin
if (PW[i] in ['0'..'9']) then begin
Error := PWContainsDigit;
Break;
end;
end;
end;
end;
case Error of
PWIsOK : writeln('Password is ok.');
PWIsBlank : writeln('Password cannot be blank.');
PWTooLong : writeln('Password is too long.');
PWContainsDigit : writeln('Password should not contain a digit');
end; { case}
until Error = PWIsOk;
writeln('Done');
end;
These are some of the things to notice:
Don't use the same error code value to represent different types of error. Using the same value for different errors just makes it more difficult for you to debug your code, because you can't tell which test gave Error the value 1.
Define constants to represent the different types of error. That way, readers don't have to wonder "What does 3 mean" in if error = 3 ...
Once you've detected a digit character in the password, there is no point examining the characters after it, hence the Break in my for loop.
If I were a user I would be annoyed not to be told what the rules are until after the program tells me I've done something wrong. Tell the use beforehand what the rules are.
Actually, it would be better to include an additional constant Unclassified with a value of say, -1, and start each iteration of the loop by assigning Error to it and, in the subsequent steps, test for Error = Unclassified rather than PWIsOk.
A case statement is a tidy and easily-maintainable way of selecting one of a number of mutually exclusive execution paths based on an ordinal value.
This is something that should be easey but I just can´t get it work.
I come from java so maby I have a error in my thinking here.
What I want to do is that I have a string with two letters like 't4' or 'pq'.
Now I just want to get each of the chracters in the string as an own string.
So I do:
firstString := myString[0];
but I don´t even get this compiled.
So I figured that they start counting form 1 and put 1 as an index.
Now I do this in a while loop and the first time I go through it it works fine. Then the second time the results are just empty or wrong numbers.
What am I missing here?
(I also tried copy but that doesn´t work either!)
while i < 10 do
begin
te := 'te';
a := te[1];
b := te[2];
i := i +1;
end;
the first loop a is 't' and b is 'e' as I would expect. The second time a is '' and b ist 't' which I don´t understand!
Strings are 1-based, not zero-based. Try the following, after adding StrUtils to your Uses list (for DupeString):
var
MyString : String;
begin
MyString := '12345';
Caption := StringOfChar(MyString[1], 8) + ':' + DupeString(Copy(MyString, 3, 2), 4);
You could split it up to mke it easier to follow, of course:
var
MyString,
S1,
S2,
S3: String;
begin
MyString := '12345';
S1 := StringOfChar(MyString[1], 8);
S2 := Copy(MyString, 3, 2);
S3 := DupeString(S2, 4);
Caption := S1 + ':' + S3;
I have a memo lines like this:
Mahogany
Unpolished
In Stock : Yes
Total Stock : 102
Redwood
Polished
In Stock : Yes
Total Stock : 80
Pine
Polished
In Stock : Yes
Total Stock : 22
And i want to have only the line of Redwood's Total Stock.
Since there are many same string of
Total Stock
I can not use this string as my keyword. So, i use "Redwood" , but i don't know how to get the line of "Total Stock" after "Redwood".
var
i: Integer;
s: string;
begin
for i := 0 to mem0.lines.Count - 1 do
begin
if (AnsiContainsStr(mem0.lines[i], 'Redwood')) then
begin
// s:= Redwood's total stock, how to do this ?
end
end;
end;
The missing code is:
s := mem0.Lines[i+3];
This makes the assumption that the format of the data is always exactly as seen in the question. If that assumption is valid then this simple code is the best solution.
It would probably make more sense to use one of the standard human readable structured data formats like JSON or YAML that have good parsers and emitters. Sadly the support for YAML on Delphi is essentially non-existant so that leaves JSON.
You could try this, but like your original code it's a bit 'fragile' in that it makes assumptions about the layout of the text you're searching (in particular that the text you're searching through is packaged in a TStrings object):
function TotalForItem(const ItemName : String; Strings : TStrings) : String;
var
i,
j,
p : Integer;
s : string;
TotalLineIntro : String;
begin
Result := '';
TotalLineIntro := 'Total Stock : ';
for i := 0 to Strings.Count - 1 do
begin
if (Pos(ItemName, Strings[i]) > 0) then
begin
for j:= i + 1 to Strings.Count - 1 do begin
p := Pos(TotalLineIntro, Strings[j]);
if p > 0 then
begin
Result := Copy(Strings[j], p + Length(TotalLineIntro), Length(Strings[j]));
exit;
end;
end;
end
end;
end;
procedure TForm3.Button1Click(Sender: TObject);
begin
ShowMessage(TotalForItem('Redwood', Memo1.Lines));
end;
Instead of AnsiContainsStr, you can use StartsWith, since the rest of the lines of the Redwood part are indentend.
var
I: Integer;
TotalStockLine: string;
MyArray: TArray<string>
begin
for I := 0 to Memo1.Lines.Count - 1 do
if Memo.Lines[I].StartsWith('Redwood') then
begin
TotalStockLine := Trim(Memo.Lines[I + 3];
Break;
end;
if TotalStockLine <> '' then
begin
MyArray := TotalStockLine.Split([':']);
TotalStock := StrToInt(Trim(MyArray[1]));
end;
etc... This can probably be simplified a little, but that shows how you could do this.
FWIW, this assumes you are using XE3 or later. Otherwise you must use the standalone StartsWith.
The program has several "encryption" algorithms. This one should blockwise reverse the input. "He|ll|o " becomes "o |ll|He" (block length of 2).
I add two strings, in this case appending the result string to the current "block" string and making that the result. When I add the result first and then the block it works fine and gives me back the original string. But when i try to reverse the order it just gives me the the last "block".
Several other functions that are used for "rotation" are above.
//amount of blocks
function amBl(i1:integer;i2:integer):integer;
begin
if (i1 mod i2) <> 0 then result := (i1 div i2) else result := (i1 div i2) - 1;
end;
//calculation of block length
function calcBl(keyStr:string):integer;
var i:integer;
begin
result := 0;
for i := 1 to Length(keyStr) do
begin
result := (result + ord(keyStr[i])) mod 5;
result := result + 2;
end;
end;
//desperate try to add strings
function append(s1,s2:string):string;
begin
insert(s2,s1,Length(s1)+1);
result := s1;
end;
function rotation(inStr,keyStr:string):string;
var //array of chars -> string
block,temp:string;
//position in block variable
posB:integer;
//block length and block count variable
bl, bc:integer;
//null character as placeholder
n : ansiChar;
begin
//calculating block length 2..6
bl := calcBl(keyStr);
setLength(block,bl);
result := '';
temp := '';
{n := #00;}
for bc := 0 to amBl(Length(inStr),bl) do
begin
//filling block with chars starting from back of virtual block (in inStr)
for posB := 1 to bl do
begin
block[posB] := inStr[bc * bl + posB];
{if inStr[bc * bl + posB] = ' ' then block[posB] := n;}
end;
//adding the block in front of the existing result string
temp := result;
result := block + temp;
//result := append(block,temp);
//result := concat(block,temp);
end;
end;
(full code http://pastebin.com/6Uarerhk)
After all the loops "result" has the right value, but in the last step (between "result := block + temp" and the "end;" of the function) "block" replaces the content of "result" with itself completely, it doesn't add result at the end anymore.
And as you can see I even used a temp variable to try to work around that.. doesnt change anything though.
I am 99.99% certain that your problem is due to a subtle bug in your code. However, your deliberate efforts to hide the relevant code mean that we're really shooting in the dark. You haven't even been clear about where you're seeing the shortened Result: GUI Control/Debugger/Writeln
The irony is that you have all the information at your fingertips to provide a small concise demonstration of your problem - including sample input and expected output.
So without the relevant information, I can only guess; I do think I have a good hunch though.
Try the following code and see if you have a similar experience with S3:
S1 := 'a'#0;
S2 := 'bc';
S3 := S1 + S2;
The reason for my hunch is that #0 is a valid character in a string: but whenever that string needs to be processed as PChar, #0 will be interpreted as a string terminator. This could very well cause the "strange behaviour" you're seeing.
So it's quite probable that you have at least one of the following 2 bugs in your code:
You are always processing 1 too many characters; with the extra character being #0.
When your input string has an odd number of characters: your algorithm (which relies on pairs of characters) adds an extra character with value #0.
Edit
With the additional source code, my hunch is confirmed:
Suppose you have a 5 character string, and key that produces block length 2.
Your inner loop (for posB := 1 to bl do) will read beyond the length of inStr on the last iteration of the outer loop.
So if the next character in memory happens to be #0, you will be doing exactly as described above.
Additional problem. You have the following code:
//calculating block length 2..6
bl := calcBl(keyStr);
Your assumption in the comment is wrong. From the implementation of calcBl, if keyStr is empty, your result will be 0.