Есть несколько проблем с вашей программой.
Ваш первый Readln
читает первую строку файла в s
, но вы не используете это значение ввсе. Это потеряно. Когда вы в первый раз делаете Readln
в цикле, вы получаете вторую строку файла (которую вы печатаете на консоли, используя Writeln
).
Ваш arr
тип записи в данном случае (и в большинстве случаев) совершенно не имеет смысла, так как это запись без каких-либо членов. Он не может хранить никакие данные, потому что у него нет членов.
В вашем цикле вы увеличиваете длину массива, по одному элементу за раз. Но вы не устанавливаете значение нового элемента на что-либо, поэтому вы делаете это напрасно. (И из-за предыдущего пункта в любом случае значение не устанавливается: элементы массива являются пустыми записями, которые не могут содержать никаких данных.)
Увеличениедлина динамического массива по одному элементу за раз очень плохая практика , потому что это может вызывать новое выделение кучи каждый раз. Весь существующий массив может потребоваться каждый раз копировать в новое место в памяти вашего компьютера.
Кажется, что содержимое цикла пытается сделать две вещи: сохранить текущуюстрока в массиве, и печать его на консоль. Я полагаю, что последний предназначен только для отладки?
Старый ввод / вывод Pascal (text
, Assign
, Reset
) устарел. Он не является поточно-ориентированным, возможно медленным, плохо обрабатывает Unicode и т. Д. Он использовался в 90-х годах, но не должен использоваться сегодня. Вместо этого используйте средства, предоставляемые вашим RTL. (Например, в Delphi вы можете использовать TStringList
, IOUtils.TFile.ReadAllLines
, потоки и т. Д.)
Частично исправленная версия кода может выглядеть следующим образом (все еще использующий ввод-вывод старой школы Pascal и неэффективную обработку массивов):
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
var
x: text;
arr: array of string;
begin
// Load file to string array (old and inefficient way)
AssignFile(x, 'D:\test.txt');
Reset(x);
try
while not Eof(x) do
begin
SetLength(arr, Length(arr) + 1);
Readln(x, arr[High(Arr)]);
end;
finally
CloseFile(x);
end;
Randomize;
// Print strings randomly
while True do
begin
Writeln(Arr[Random(Length(Arr))]);
Readln;
end;
end.
Если вы хотите решить проблему с неэффективным массивом, но по-прежнему не используете современные классы, выделите их по частям:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils;
var
x: text;
s: string;
arr: array of string;
ActualLength: Integer;
procedure AddLineToArr(const ALine: string);
begin
if Length(arr) = ActualLength then
SetLength(arr, Round(1.5 * Length(arr)) + 1);
arr[ActualLength] := ALine;
Inc(ActualLength);
end;
begin
SetLength(arr, 1024);
ActualLength := 0; // not necessary, since a global variable is always initialized
// Load file to string array (old and inefficient way)
AssignFile(x, 'D:\test.txt');
Reset(x);
try
while not Eof(x) do
begin
Readln(x, s);
AddLineToArr(s);
end;
finally
CloseFile(x);
end;
SetLength(arr, ActualLength);
Randomize;
// Print strings randomly
while True do
begin
Writeln(Arr[Random(Length(Arr))]);
Readln;
end;
end.
Но если у вас есть доступ к современным классам, все становится намного проще. В следующих примерах используется современный RTL Delphi:
Общий TList<T>
автоматически обрабатывает эффективное расширение:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Generics.Defaults, Generics.Collections;
var
x: text;
s: string;
list: TList<string>;
begin
list := TList<string>.Create;
try
// Load file to string array (old and inefficient way)
AssignFile(x, 'D:\test.txt');
Reset(x);
try
while not Eof(x) do
begin
Readln(x, s);
list.Add(s);
end;
finally
CloseFile(x);
end;
Randomize;
// Print strings randomly
while True do
begin
Writeln(list[Random(list.Count)]);
Readln;
end;
finally
list.Free;
end;
end.
Но вы можете просто использовать TStringList
:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, Classes;
var
list: TStringList;
begin
list := TStringList.Create;
try
list.LoadFromFile('D:\test.txt');
Randomize;
// Print strings randomly
while True do
begin
Writeln(list[Random(list.Count)]);
Readln;
end;
finally
list.Free;
end;
end.
Или вы можете сохранить массив и использовать IOUtils.TFile.ReadAllLines
:
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils, IOUtils;
var
arr: TArray<string>;
begin
arr := TFile.ReadAllLines('D:\test.txt');
Randomize;
// Print strings randomly
while True do
begin
Writeln(arr[Random(Length(arr))]);
Readln;
end;
end.
Как видите, современные подходы намного удобнее (меньше кода). Они также работают быстрее и обеспечивают поддержку Юникода.
Примечание: Все приведенные выше фрагменты предполагают, что файл содержит хотя бы одну строку. Они потерпят неудачу, если это не так, и в реальном / производственном коде вы должны проверить это, например, как
if Length(arr) = 0 then
raise Exception.Create('Array is empty.');
или
if List.Count = 0 then
raise Exception.Create('List is empty.');
до// Print strings randomly
часть, которая предполагает , что массив / список не пуст.