Not overloading operator - pascal

Good day, I'm doing some Codeforces exercises in my free time, and I had a problem to test if the user was a boy or a girl, well, my problem isn't that, i have just demonstrated the code.
While compiling my code in my computer ( I'm using version 3.0.4 for i386 ) i get no error, but codeforces gives me this error
program.pas(15,16) Error: Operator is not overloaded: "freq(Char;AnsiString):LongInt;" + "ShortInt"
program.pas(46,4) Fatal: There were 1 errors compiling module, stopping
The error wasn't clear enough to me, as the same script was perfectly compiled with my version.
The platform is using ( version 3.0.2 i386-Win32 ).
program A236;
uses wincrt, sysutils;
var
username : String;
function freq(char: char; username : String): Integer;
var
i: Integer;
begin
freq:= 0;
for i:= 1 to length(username) do
if char = username[i] then
freq:= freq + 1;
//writeln(freq);
end;
function OddUserName(username : String): Boolean;
var
i, counter: Integer;
begin
OddUserName:= false; // even
counter:= 0;
for i:= 1 to length(username) do
if freq(username[i], username) <> 1 then
delete(username, i, 1)
else
counter:= counter + 1;
if counter mod 2 <> 0 then
OddUserName:= true; // odd
//writeln(counter);
//writeln(OddUserName);
end;
begin
readln(username);
if not OddUserName(username) then
writeln('CHAT WITH HER!')
else
writeln('IGNORE HIM!');
//readkey();
end.
The error is supposed to be at this line probably :
function freq(character: char; username : String): Integer;
Thanks for everyone who helps.

Inside of a function, the function's name can be used as a substitute for using an explicit local variable or Result. freq() and OddUserName() are both doing that, but only freq() is using the function name as an operand on the right-hand side of an assignment. freq := freq + 1; should be a legal statement in modern Pascal compilers, see Why i can use function name in pascal as variable name without definition?.
However, it would seem the error message is suggesting that the failing compiler is treating freq in the statement freg + 1 as a function type and not as a local variable. That would explain why it is complaining about not being able to add a ShortInt with a function type.
So, you will have to use an explicit local variable instead, (or the special Result variable, if your compiler provides that), eg:
function freq(charToFind: char; username : String): Integer;
var
i, f: Integer;
begin
f := 0;
for i := 1 to Length(username) do
if charToFind = username[i] then
f := f + 1;
//writeln(f);
freq := f;
end;
function freq(charToFind: char; username : String): Integer;
var
i: Integer;
begin
Result := 0;
for i := 1 to Length(username) do
if charToFind = username[i] then
Result := Result + 1;
//writeln(f);
end;

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.

Error : Operator is not overloaded

I've created a simple block of code using Free Pascal to validate an ID number such as Abc123 being input.
When I try to run the program I get an error saying, "Operator is not overloaded" at the points where it says,
IF not (Ucase in Upper) or (Lcase in Lower) or (Num in Int) then
Specifically where the "in" appears.
Does anyone have any idea why the error occurs and what I can do to solve it?
Thanks!
Program CheckChar;
VAR
UserID, LCase, UCase, Num : String;
readkey : char;
L : Integer;
CONST
Upper = ['A'..'Z'];
Lower = ['a'..'z'];
Int = ['0'..'9'];
Begin
Write('Enter UserID ');Readln(UserID);
Ucase := Copy(UserID,1,1);
LCase := Copy(UserID,2,1);
Num := Copy(UserID,3,2);
L := Length(UserID);
While L = 6 Do
Begin
IF not (Ucase in Upper) or (Lcase in Lower) or (Num in Int) then
Begin
Writeln('Invalid Input');
End;
Else
Writeln('Input is valid');
End;
readln(readkey);
End.
in is used to test the presence of an element in a set. Here you set is a set of char, so the element to test must also be a char. In your sample the elements you tested were some strings (UCase, LCase and Num) which caused the error message.
You have to use a slice of Ucase and LCase of length one or you can also directly take a single character (astring[index]) instead of copying with Copy.
Also your while loop is totally useless. You have to test only 6 characters so let's unroll the loop instead of puting some complexity while obsvioulsy you just start learning.
Finally, one way to write your checker correctly is so:
Program CheckChar;
var
UserID : string;
readkey : char;
L : Integer;
invalid: boolean;
const
Upper = ['A'..'Z'];
Lower = ['a'..'z'];
Int = ['0'..'9'];
begin
Write('Enter UserID ');
Readln(UserID);
L := length(UserId);
if L <> 6 then invalid := true
else
begin
invalid := not (UserID[1] in Upper) or // take a single char, usable with in
not (UserID[2] in Lower) or // ditto
not (UserID[3] in Lower) or // ditto
not (UserID[4] in Int) or // ditto
not (UserID[5] in Int) or // ditto
not (UserID[6] in Int); // ditto
end;
if invalid then
Writeln('Invalid Input')
else
Writeln('Input is valid');
readln(readkey);
end.

Trying and failing to CryptProtectMemory / CryptUnprotectMemory in Delphi xe10

I have tried the following code (and varients) without any sucess, nor can I find any examples of how to call these Windows Functions from Delphi out there. Any clues would be very gratefully received.
The CryptProtectMemory does appear to produce some possibly encrypted result, but the unprotect does not change that result at all.I suspect I have done something charactisticly stupid, but I havent found it all day...
function WinMemEnc(PlnTxt: String): String;
var
Enc: Pointer;
j: Integer;
EncSze: Cardinal;
ws: String;
const
CRYPTPROTECTMEMORY_SAME_PROCESS: Cardinal = 0;
EncryptionBlockSize: Integer = 8;
begin
if Length(PlnTxt) mod EncryptionBlockSize = 0 then
j := Length(PlnTxt)
else
j := ((Length(PlnTxt) div 8) + 1) * 8;
ws := StringofChar(' ', j);
Move(PlnTxt[1], ws[1], j);
Enc := Pointer(ws);
EncSze := j * 2;
if CryptProtectMemory(Enc, EncSze, CRYPTPROTECTMEMORY_SAME_PROCESS) then
begin
Setlength(Result, j);
Move(Enc, Result[1], EncSze);
end;
end;
function WinMemDcr(EncInp: String): String;
var
Enc: Pointer;
j: Integer;
EncSze: Cardinal;
ws: String;
const
CRYPTPROTECTMEMORY_SAME_PROCESS: Cardinal = 0;
begin
j := Length(EncInp);
EncSze := j * 2;
ws := EncInp;
Enc := Pointer(ws);
if CryptUnprotectMemory(Enc, EncSze, CRYPTPROTECTMEMORY_SAME_PROCESS) then
begin
Setlength(Result, j);
Move(Enc, Result[1], EncSze);
end;
end;
You have set EncryptionBlockSize := 8; while in my library CRYPTPROTECTMEMORY_BLOCK_SIZE = 16.
You also mistakenly move only half of the input string to ws, because j holds the length of the string while Move() moves Count number of bytes. A Unicode Char is 2 bytes.
As said in the comments, encryption/decryption works on bytes and storing an encryption in a string is a potential disaster.
So here's my suggestion for a encryption/decryption of a string with encrypted storage in TBytes.
function MemEncrypt(const StrInp: String): TBytes;
begin
Result := TEncoding.Unicode.GetBytes(StrInp);
if Length(Result) mod CRYPTPROTECTMEMORY_BLOCK_SIZE <> 0 then
SetLength(Result, ((Length(Result) div CRYPTPROTECTMEMORY_BLOCK_SIZE) + 1) * CRYPTPROTECTMEMORY_BLOCK_SIZE);
if not CryptProtectMemory(Result, Length(Result), CRYPTPROTECTMEMORY_SAME_PROCESS) then
raise Exception.Create('Error Message: '+IntToStr(GetLastError));
end;
function MemDecrypt(const EncInp: TBytes): String;
var
EncTmp: TBytes;
begin
EncTmp := Copy(EncInp);
if CryptUnprotectMemory(EncTmp, Length(EncTmp), CRYPTPROTECTMEMORY_SAME_PROCESS) then
result := TEncoding.Unicode.GetString(EncTmp)
else
raise Exception.Create('Error Message: '+IntToStr(GetLastError));
end;
In the decryption a copy of the input TBytes is made to preserve the encrypted data.
And finally a test procedure:
procedure TForm13.Button2Click(Sender: TObject);
const
Txt = '1234567890123456789012345678901';
var
s: string;
b: TBytes;
begin
s := Txt;
Memo1.Lines.Add(s);
b := MemEncrypt(Txt);
s := MemDecrypt(b);
Memo1.Lines.Add(s);
end;
Without testing it (purely from the looks of your code), I believe the problem lies in the MOVE statement:
Move(Enc, Result[1], EncSze);
You are moving data from the location of the pointer - not from the data that the pointer is pointing to.
You should use
Move(Enc^, Result[1], EncSze);
to move data from the location that is POINTED TO by the pointer, and not from the pointer itself.
To clarify: The Enc variable is - say - located at address $12345678 and the data you are manipulating is located at address $99999999
This means that at address $12345678 is located 4 bytes ($99 $99 $99 and $99). And at address $99999999 is located the data you are manipulating.
The statement
Move(Enc, Result[1], EncSze);
thus moves EncSize bytes from the address $12345678 to the 1st character of the string variable Result. This you do not want, as it will only move 4 bytes of $99 and then whatever follows at address $1234567C and on.
To move data from the address $99999999 you need to tell the compiler, that you want to move data from the location POINTED TO by the pointer, and not from the POINTER itself:
Move(Enc^, Result[1], EncSze);
But other that that, I agree with David. You should stop using strings as storage for non-string data. It'll bite you in the a** at some point. Use a byte array (TBytes) instead.

Pascal error 'call by var for arg no.1 has to match exactly'

I learning to make a program that gets data from a txt file and places it in arrays.
the following are its types :
type
ekspedisi = record
nmeksp : string; // Nama Ekspedisi
jlp : string; // Jenis layanan pengiriman
biaya : integer; // Biaya pengiriman per kg
lp : integer; // per hari
end;
ekspedisiku = record
nom : array [1..100] of ekspedisi;
end;
and a simple algorithm
procedure getDaftarEkspedisi(var kirim : ekspedisiku);
var
i,j,k : integer;
eksp : text;
init : string;
garis : array [1..100] of integer;
mark : string;
jeks : integer;
count : integer;
begin
assign(eksp,'ekspedisi.txt');
reset(eksp);
i := 0;
k := 1;
j := 1;
mark := '|';
jeks := 10;
writeln('Loading ekspedisi.. ');
while(not(eof(eksp))) do
begin
readln(eksp,init);
i := i + 1;
for j := 1 to length(init) do
begin
if init[j] = mark then
begin
garis[k] := j;
k := k + 1;
end;
end;
for i := 1 to jeks do
begin
count := ((i-1)*5);
kirim.nom[i].nmeksp := copy(init,garis[1+count] + 2,garis[2+count]-garis[1+count]-2);
kirim.nom[i].jlp := copy(init,garis[2+count] + 2,garis[3+count]-garis[2+count]-2);
val(copy(init,garis[3+count] + 2,garis[4+count]-garis[3+count]-2),kirim.nom[i].biaya);
val(copy(init,garis[4+count] + 2,garis[5+count]-garis[4+count]-2),kirim.nom[i].lp);
end;
close(kirim);
writeln('loading sukses.');
end;
end;
from that code, i get the following error
<166,13>Error: Call by var for arg no.1 has to match exactly : got "ekspedisiku" expected "Text"
curiously, line 166 is only
close(kirim);
any help is appreciated.
You need to pass the file handle to close, so:
close(kirim);
should be:
close(eksp);
It also looks like you're closing the file at the wrong place in your function. It should most likely be after the while loop, so you need to change:
close(kirim);
writeln('loading sukses.');
end;
end;
to:
end;
close(kirim);
writeln('loading sukses.');
end;
Note that this mistake probably happened because your identation is messed up - if you're careful with formatting your code properly then you won't be so likely to make this kind of error.

How to convert a string version value to a numerical value in Inno Setup Scripts?

I want to develop a setup package for conditionally upgrading an existing package. I want to check the existing software version against to-be-installed version. In order to do that, I have to compare the version strings.
How can I convert the string value to a numerical value in a Inno setup script?
RegQueryStringValue(HKEY_LOCAL_MACHINE, 'Software\Blah blah', 'Version', version)
version = 'V1.R2.12';
numVersion := ??string_to_numerical_value??(version);
This is a little more tricky, as you would want to handle versions like 'V1.R2.12' and 'V0.R15.42' correctly - with the simple conversion in the other answer you would get 1212 and 1542, which would not compare the way you would expect.
You need to decide how big each part of the version number can be, and multiply the parts by that value to get a correct end number. Something like this:
[Code]
function string_to_numerical_value(AString: string; AMaxVersion: LongWord): LongWord;
var
InsidePart: boolean;
NewPart: LongWord;
CharIndex: integer;
c: char;
begin
Result := 0;
InsidePart := FALSE;
// this assumes decimal version numbers !!!
for CharIndex := 1 to Length(AString) do begin
c := AString[CharIndex];
if (c >= '0') and (c <= '9') then begin
// new digit found
if not InsidePart then begin
Result := Result * AMaxVersion + NewPart;
NewPart := 0;
InsidePart := TRUE;
end;
NewPart := NewPart * 10 + Ord(c) - Ord('0');
end else
InsidePart := FALSE;
end;
// if last char was a digit the last part hasn't been added yet
if InsidePart then
Result := Result * AMaxVersion + NewPart;
end;
You can test this with the following code:
function InitializeSetup(): Boolean;
begin
if string_to_numerical_value('V1.R2.12', 1) < string_to_numerical_value('V0.R15.42', 1) then
MsgBox('Version ''V1.R2.12'' is not as recent as version ''V0.R15.42'' (false)', mbConfirmation, MB_OK);
if string_to_numerical_value('V1.R2.12', 100) > string_to_numerical_value('V0.R15.42', 100) then
MsgBox('Version ''V1.R2.12'' is more recent than version ''V0.R15.42'' (true)', mbConfirmation, MB_OK);
Result := FALSE;
end;
Whether you pass 10, 100 or 1000 for AMaxVersion depends on the number and range of your version number parts. Note that you must not overflow the LongWord result variable, which has a maximum value of 2^32 - 1.
I haven't tried that (and my Pascal knowledge is a bit rusty), but something like the following should work:
function NumericVersion(s: String): Integer;
var
i: Integer;
s1: String;
begin
s1 := '';
for i := 0 to Length(s)-1 do
if (s[i] >= '0') and (s[i] <= '9') then
s1 := s1 + s[i];
Result := StrToIntDef(s1, 0);
end;
Please not that you'll have to play with the start and end value for i as I'm not sure whether it is zero-based or not (s[0] may contain the length of the string if it is a "Pascal String").
I've implemented two version strings (actually one string and one dword value) in the registry to overcome complexity.
displayversion="v1.r1.0"
version="10100" (=1*10^4 + 1*10^2 + 0*10^0)
That's simple. Though not an answer to this question, however one might think the other way around when faced with complexity, which could be avoided in a simpler way.

Resources