How do I use a file with numbers on each line as an input (not a .txt file), do some calculations, then write the results into another file in pascal? - file-handling

I am trying to make a program that will take a file called "root.inp" (file contains 3 integers) as its input, with each integer in a separate line, and then perform the operation shown in picture:
(a, b, c are integers in said input file, s is the result)
After that, I want to be able to write whatever is inside S into a new file called "root.out". But after some time, I cannot find a solution to this. Here's what I've tried
program Formula;
type
input = record
firstnum: integer;
secondnum: integer;
thirdnum: integer;
end;
output = record
sum: real;
end;
var
f: file of input;
p: file of output;
S: real;
a, b, c: integer;
begin
assign (f, 'root.inp');
reset (f);
while not eof(f) do;
begin
read (f,input);
a := input.firstnum;
b := input.secondnum;
c := input.thirdnum;
S := (a*a + b*b + c*c) / (a*b*c) + sqrt(a*b*c);
end;
close (f);
assign (p, 'root.out');
rewrite(p);
output.sum := S;
write(p, output);
close(p);
end.
After executing, the 'root.out' file is completely blank, but the program did not show any error while compiling. I cannot find a cause for this.

The main problem is the semicolon after while not eof (f) do - this should be removed. The semicolon prevents the loop from running. Also, what is file of input and input.firstnum? Standard Pascal would have three separate read commands, one for each variable.

You need to define variables of the type input and output, and use them for reading and writing, rather than the type names.
P.S. input and output are predefined identifiers in Pascal's runtime library, better avoid them.

Related

Equivalent of C++ ( cin >> n) in delphi

I need to read an unknown amount of integer numbers from standard input. I know that in C++ it can be easily realized by:
while ( cin >> n )
Is there any way to realise the same in Delphi?
This should be quite near:
while not EoF do
Read(n);
You can also write your own CIn function to catch any wrong input like this:
function CIn(out I: Integer): Boolean;
begin
Result := False;
if not EoF then begin
try
Read(I);
except
Exit;
end;
Result := True;
end;
end;
Note that Read will only return when a line ending or end of file is available. So when you have multiple integers in one line, the first one is returned when the line end is given.
I should also mention that the behavior of the C++ function is way more complex than what can be accomplished with Delphi built-in methods. It would help if you can list the specific requirements for your task to give a solid solution.

Delphi Code not reading a text file

I am having problems with the following code:
var l :string;
var f:Textfile;
begin
assignfile(f,'c:\test\file.txt');
reset(f);
while not eof(f) do
readln(f,l);
closefile(f);
showmessage(l);
My problem is that the showmessage is returning nothing ... it's empty and the text file is not empty.
Why is this happening?
In addition to what has already been said:
Nobody uses lowlevel file routines these days. There are at least two much better approaches. The first involves streams, the second a TStringList:
var sl : TStringList;
begin
sl := TStringList.Create;
try
sl.LoadFromFile('c:\your\file.txt');
ShowMessage(sl.Text);
finally
sl.Free;
end;
end;
Disclaimer: code not tested
You read each new line in the same variable, thereby overwriting the last line with a new line. If the last read returns an empty string (or a string with just spaces or an enter), your message box will seem empty. And even if it were not empty, it would show only the last line, not the whole file.
Use l as a buffer. After each read, append it to another string:
var l: string;
var t: string;
var f: Textfile;
begin
t := '';
assignfile(f, 'c:\test\file.txt');
reset(f);
while not eof(f) do
begin
readln(f, l);
t := t + l;
end;
closefile(f);
showmessage(t);
For large files it is more efficient to use a String Builder instead of concatting everything to the same string, because t will be reallocated on each iteration.
Alternatively, use a TStringList or a TFileStream to read files. Using a TFileStream, you can read the whole file at once into a string. The TStringList has the advantage that it parses the file and makes each line an item in the stringlist.
But with those solutions, you are going to read the whole file into memory. If you can process it line by line, do the processing inside the loop.
The line
Readln(f, l)
puts the current line of f into the string l. Hence it will replace the previous line, so at the end, l will only contain the last line of the file. Maybe the last line is empty?
Most likely the last line of your file is empty.
You may want to try the following to see the difference:
while not eof(f) do
begin
readln(f,l);
showmessage(l);
end;
closefile(f);

How to Call Function from Fortran DLL in delphi?

I have problem with delphi code. I want to call the function in delphi to process the fortran function, but I have transferred to DLL. Here is code Fortran
SUBROUTINE c_zsn(m,d,k,f,zsn,nf)
! Specify that the routine name is to be made available to callers of the
! DLL and that the external name should not have any prefix or suffix
!MS$ ATTRIBUTES DLLEXPORT :: c_zsn
!MS$ ATTRIBUTES ALIAS:'c_zsn' :: c_zsn
!MS$ ATTRIBUTES VALUE :: m,d,k,nf
!MS$ ATTRIBUTES REFERENCE :: f,zsn
IMPLICIT NONE
INTEGER :: nf,i
REAL(KIND(0.D0)) :: m,d,k,f(0:(nf-1)),zsn(0:(nf-1)),om,pi
COMPLEX(KIND(0.D0)) :: j
j = (0.d0, 1.d0)
pi = 4.d0 * datan(1.d0)
do i=0,nf-1
om = 2.d0*pi*f(i)
zsn(i) = abs(-om**2*m-j*om*d+k)
end do
END SUBROUTINE
and here is code for the Delphi that I used
procedure TForm1.Button2Click(Sender: TObject);
type tarray=array[0..10]of double;
var a:thandle;
fcn:function(s,d,f:double;var g,h:tarray;n:integer):double;
e,f,d,g,h,i,j:double;
k:tarray;
l,o:tarray;
n,m:integer;
begin
a:=LoadLibrary('dllsub.dll');
if (A=0) then
begin
Application.MessageBox('Failed to open library','Error', MB_OK or MB_ICONEXCLAMATION);
exit;
end;
#fcn:=GetProcAddress(a, 'c_zsn');
if #b=nil then
begin
ShowMessage('Failed to open function');
exit;
end;
e:=2;
f:=200;
d:=0.01;
n:=10;
for m:=0 to n do
l[m]:=m;
fcn(e,d,f,l,o,n); // this is the problem
FreeLibrary(a);
end;
I cannot call the function (the bold one).
I would declare the function like this:
procedure c_zsn(
m: Double;
d: Double;
k: double;
f: PDouble;
zsn: PDouble;
n: Integer
); stdcall; external 'dllsub.dll';
You do need to specify the calling convention. You omitted that which meant that your code used the default register calling convention which is private to Delphi. I'm guessing that the calling convention is stdcall but it may be cdecl. Check the compiler documentation to be sure.
And it is not at all obvious to me why you declared a function that returns a double. The Fortran does not do that.
Other than that I changed the parameter names to match the Fortran code. I also switched to load time linking which is easier to code against. You can skip the calls to LoadLibrary and GetProcAddress and let the loader resolve the linkage.
Finally, I think the two arrays are better passed as PDouble (that is pointer to Double) rather than committing at compile time to fixed size arrays.
You can call the function like this:
c_zsn(e,d,f,#l[0],#o[0],n);
Do note that you have declared arrays of length 11 rather than length 10. Did you mean to do that? I think you should declare the arrays like this:
var
l, o: array [0..9] of Double;
One final point is that the Fortran code is very simple. It would be very easy indeed to translate it into Delphi.

Searching a sorted TStringList for an entry with a prefix (StartsText)

I have a TStringList which is sorted and contains unique filenames. The list can be of any size (so it can be hundreds of thousands of entries). I want to check to see if any of the entries start with a particular string (i.e. if the files are in a sub-folder). It's easy enough serially scanning the list and using StartsText, but that isn't an ideal solution.
Using the TStringList.Find() code as a starting point, I've created a function which I think is the solution, but I want to be sure. Don't worry about the following not being a member of the class (FList is the TStringList instance being searched), and StartsFilename works the same way as StartsText:
function ShortcutFind(const S: string): Boolean;
var
L, H, I, C: Integer;
begin
Result := False;
L := 0;
H := FList.Count - 1;
while L <= H do begin
I := (L + H) shr 1;
if TFilenameUtils.StartsFilename(FList[I], aFolder) then begin
Result:=TRUE;
Exit;
end;
C := FList.CompareStrings(FList[I], S);
if C < 0 then
L := I + 1
else begin
H := I - 1;
if C = 0 then begin
Result := True;
if FList.Duplicates <> dupAccept then L := I;
end;
end;
end;
end;
Basically, the only real change is that it does the check before moving onto the next entry to compare.
Note that switching from TStringList is not an option.
Would this method work?
Thanks
If TFilenameUtils.StartsFilename is the same as StartsText (and your first paragraph suggests it might be), then you can do the whole function in one statement by using TStringList.Find instead of copying it:
var
I: Integer;
begin
Assert(not FList.CaseSensitive);
Result := FList.Find(S, I) or ((I < FList.Count) and StartsText(S, FList[I]));
end;
That should work because when Find fails, it still tells you the index of where the desired string would have appeared in the list. When you search for your prefix string, its location will be before any other strings that start with that prefix, so if there are any strings with that prefix, they'll appear immediately after the hypothetical location of the prefix itself.
If you want to keep your current code, then you can simplify it by removing the conditional that checks C = 0. That condition should never occur, unless your StartsFilename function is broken. But, if the function really is broken and C can be zero, then you can at least stop executing the loop at that point since you've found what you were looking for. Either way, you don't need to check Duplicates since your function doesn't have the same requirement as Find does to return the index of the found item.

Any way to get TStringList.CommaText to not escape commas with quotes?

I'm doing some work with code generation, and one of the things I need to do is create a function call where one of the parameters is a function call, like so:
result := Func1(x, y, Func2(a, b, c));
TStringList.CommaText is very useful for generating the parameter lists, but when I traverse the tree to build the outer function call, what I end up with looks like this:
result := Func1(x, y, "Func2(a, b, c)");
It's quoting the third argument because it contains commas, and that produced invalid code. But I can't do something simplistic like StringReplace all double quotes with empty strings, because it's quite possible that a function argument could be a string with double quotes inside. Is there any way to make it just not escape the lines that contain commas?
You could set QuoteChar to be a space, and you'd merely get some extra spaces in the output, which is generally OK since generated code isn't usually expected to look pretty. String literals would be affected, though; they would have extra spaces inserted, changing the value of the string.
Free Pascal's TStrings class uses StrictDelimiter to control whether quoting occurs when reading the DelimitedText property. When it's true, quoting does not occur at all. Perhaps Delphi treats that property the same way.
Build an array of "unlikely characters" : non-keyable like †, ‡ or even non-printable like #129, #141, #143, #144.
Verify you don't have the 1st unlikely anywhere in your StringList.CommaText. Or move to the next unlikely until you get one not used in your StringList.CommaText. (Assert that you find one)
Use this unlikely char as the QuoteChar for your StringList
Get StringList.DelimitedText. You'll get the QuoteChar around the function parameters like: result := Func1(x, y, †Func2(a, b, c)†);
Replace the unlikely QuoteChar (here †) by empty strings...
What about using the Unicode version of AnsiExtractQuotedStr to remove the quotes?
Write your own method to export the contents of your TStringList to a string.
function MyStringListToString(const AStrings: TStrings): string;
var
i: Integer;
begin
Result := '';
if AStrings.Count = 0 then
Exit;
Result := AStrings[0];
for i := 1 to AStrings.Count - 1 do
Result := Result + ',' + AStrings[i];
end;
Too obvious? :-)
Alternatively, what would happen if you set StringList.QuoteChar to #0 and then called StringList.DelimitedText?
We have written a descendant class of TStringList in which reimplemented the DelimitedText property. You can copy most of the code from the original implementation.
var
LList: TStringList;
s, LOutput: string;
begin
LList := TStringList.Create;
try
LList.Add('x');
LList.Add('y');
LList.Add('Func2(a, b, c)');
for s in LList do
LOutput := LOutput + s + ', ';
SetLength(LOutput, Length(LOutput) - 2);
m1.AddLine('result := Func1(' + LOutput + ')');
finally
LList.Free;
end;
end;
Had the same problem, here's how I fixed it:
s := Trim(StringList.Text)
that's all ;-)

Resources