I' m working with Win 10 64bits and Lazarus 1.6, the LineEnding is just a constant #13#10 in the System unit. Btw here the SetBufText approach take more time to execute than my approach, also, I don't got any compiler error and the final file is correct. Maybe a few diferences between SO and my PC specs cause this.
PC Spec
Laptop Asus S400CA
Core I3 1.5ghz (2 Cores + 2 HT)
6GB DDR3 1600mhz
SSD 120gb PNY
Similar computer here, but desktop, not laptop. Using Manjaro Linux 64bits. According to
documentation, LineEnding is system dependent.
In my case (Linux), it's just a #10, so it may be seen as char, while your Windows' #13#10 is a string.
Anyways, the wrong file I was getting was my fault, wrong adaption of your code. I realized later. Here the correct one:
procedure .arrayToFile(numbers: Array of Integer; fileName: String);
var
Data: TMemoryStream;
I, Ll: Integer;
P, S: Pointer;
V, L: string;
begin
Data := TMemoryStream.Create;
L := LineEnding; // <-- if it's a char, now it's a string
Ll:= Length(LineEnding); // <-- 1 on *NIX, 2 on windows
Data.SetSize((11 + Ll) * Length(numbers)); //<-- as numbers are signed 32bits integers, they will never be longer than 11digits + LineEnding
P := Data.Memory;
S := P;
for I := 0 to High(numbers) do
begin
System.Str(numbers[i], V);
Move(V[1], P^, Length(V));
P += Length(V);
Move(L, P^, Ll);
P += Ll; // <--- my error was here, leaving your "2"; that's the wrong thing with magic numbers :)
end;
Data.SetSize(P - S);
Data.SaveToFile(fileName);
Data.Free;
end;
FreePascal faster than Java? Well, I expected so, but it seems I have to do some tricks to achieve that!
I did the reverse-way function (fileToArray), and to get a fast result, I had to write my own StrToInt. Special one, because it reads.
First attempt, with TStringList + StrToInt loop, 2.8s
function fileToArray(fileName: String): TIntArray;
var
list: TStrings;
i, len: Integer;
begin
list := TStringList.Create;
list.LoadFromFile(fileName);
len := list.Count - 1;
setLength(result, len + 1);
for i := 0 to len do
begin
result[i] := StrToInt(list[i]);
end;
list.Free;
end;
Second attempt, using AssignFile, ReadLn, ... 1.2~1.6s. The magic number makes a lot!!! big one is faster. SetTextBuf is relevant, but not that much.
function fileToArray(fileName: String): TIntArray;
var
F: TextFile;
i, bufs, s: Integer;
buf: array[0..65535] of char;
begin
AssignFile(F, fileName);
SetTextBuf(F, buf[0], sizeof(buf));
Reset(F);
i := 0; bufs := 1; s := bufs*10000000;
SetLength(result, s);
while not eof(F) do
begin
ReadLn(F, result[i]);
Inc(i);
if i = s then
begin
Inc(bufs);
s := bufs*10000000;
SetLength(result, s);
end;
end;
CloseFile(F);
setLength(result, i);
end;
And the winner, using a stream, fastest but bigger, algo more memory footprint, 350ms!!!!!!
function fileToArray(fileName: String): TIntArray;
var
fs: TFileStream;
i, bufs, s: Integer;
n, num, sign: Integer;
ss: string; c:char;
skip: boolean;
begin
fs := TFileStream.Create(fileName, fmOpenRead);
setLength(ss, fs.size);
fs.read(ss[1], fs.size);
fs.free;
i := 0; bufs := 1; s := bufs*10000000;
SetLength(result, s);
num := 0; sign := 1; skip := true;
for n := 1 to high(ss) do
begin
c := ss[n];
if c = '-' then
begin
skip := false;
sign := -1;
end
else if c in ['0'..'9'] then
begin
skip := false;
num := num*10 + (ord(c) - 48);
end
else if not skip then
begin
skip := true;
result[i] := num * sign;
sign := 1; num := 0;
Inc(i);
if i = s then
begin
Inc(bufs);
s := bufs*10000000;
SetLength(result, s);
end;
end;
end;
setLength(result, i);
end;
The Java version is not that fast, but still competes and much cleaner. It takes about 900ms.
public int[] fileToArray(String fileName) throws IOException
{
try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
int[] res = br.lines()
.mapToInt(Integer::parseInt)
.toArray();
return res;
}
}
Comparisons are not fair, anyways. Java hasn't the compatibility with old code and Delphi and other stuff, and FreePascal hasn't the Java budget.
What do you say?
Regards.