您的程序存在一些问题。
您的第一个Readln 将文件的第一行读入s,但您根本不使用此值。它丢失了。第一次在循环中执行Readln 时,您会得到文件的第二行(使用Writeln 将其打印到控制台)。
您的arr 记录类型在这种情况下(在大多数情况下)完全没有意义,因为它是没有任何成员的记录。它不能存储任何数据,因为它没有成员。
在您的循环中,您扩展数组的长度,一次一项。但是您没有将新项目的值设置为任何值,因此您这样做是徒劳的。 (而且,由于前面的一点,在任何情况下都不需要设置任何值:数组的元素是不能包含任何数据的空记录。)
每次增加一个动态数组的长度是very bad practice,因为它可能每次都会导致新的堆分配。每次都可能需要将整个现有阵列复制到计算机内存中的新位置。
循环的内容似乎试图做两件事:将当前行保存在数组中,并将其打印到控制台。我假设后者仅用于调试?
旧式 Pascal I/O(text、Assign、Reset)已过时。它不是线程安全的,可能很慢,处理 Unicode 不好等等。它在 90 年代使用过,但今天不应该使用。相反,请使用 RTL 提供的设施。 (例如,在 Delphi 中,您可以使用 TStringList、IOUtils.TFile.ReadAllLines、流等)
部分固定的代码版本可能如下所示(仍然使用老式的 Pascal I/O 和低效的数组处理):
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.
但是,如果您可以使用现代课程,事情就会变得容易得多。以下示例使用现代 Delphi RTL:
通用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.
如您所见,现代方法更方便(代码更少)。它们也更快,并为您提供 Unicode 支持。
注意: 上面所有的 sn-ps 都假定文件至少包含一行。如果不是这种情况,它们将失败,并且在实际/生产代码中,您必须验证这一点,例如喜欢
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 部分之前,假设数组/列表不为空。