在 Pascal 中将文本文件中的字符串读入数组
Reading String from a text file into Array in Pascal
使用这个程序,我试图读取一个文件并将其随机打印到控制台。我想知道是否必须为此使用数组。例如,我可以将我的字符串分配到一个数组中,然后从我的数组中随机打印。但是,我不确定如何处理。另一个问题是,我当前的程序没有从我的文件中读取第一行。我有一个包含
的文本文件 text.txt
1. ABC
2. ABC
...
6. ABC
下面是我的代码。
type
arr = record
end;
var
x: text;
s: string;
SpacePos: word;
myArray: array of arr;
i: byte;
begin
Assign(x, 'text.txt');
reset(x);
readln(x, s);
SetLength(myArray, 0);
while not eof(x) do
begin
SetLength(myArray, Length(myArray) + 1);
readln(x, s);
WriteLn(s);
end;
end.
请告诉我如何解决这个问题!
我在清楚地阅读您的问题后编辑了答案。
尽管如此,对于您阅读额外行的问题,您在进入阅读循环之前碰巧阅读了一行。所以这是你的程序从开始到结束的语句,没有额外的 readln()。
这段代码套路很简单,其实我能想到的方法不止一种。对于第一种方法,您可以将每一行读入一个数组。然后遍历数组并创建一个 0 或 1 的随机数。如果是 1,则打印该行。
对于第二种方法,每次从文件中读取一行,生成一个0或1的随机数。如果该随机数是1,则打印该行。
请注意在 运行 random() 之前使用 randomize,以免获得与上次程序执行相同的随机数。另一件需要考虑的事情是,如果你要处理一个大文本文件,每个设置的长度都会花费很多。最好跟踪那里发生的事情并将 20 - 30 长度设置为 1 甚至 100。如果你使用数组路由。
下面的代码是数组的方法,对于不使用数组的方法,看了下面的例程就很简单了
var
x: text;
SpacePos: word;
myArray: array of string;
i: integer;
begin
Randomize;
Assign(x, 'text.txt');
reset(x);
SetLength(myArray, 0);
i := 0;
while not eof(x) do
begin
SetLength(myArray, Length(myArray) + 1);
readln(x, myArray[i]);
i := i + 1;
end;
for i:= 0 to Length(myArray) - 1 do
begin
if random(2) = 1 then
WriteLn(myArray[i]);
end;
end.
Also another problem is that, my current program does not read the first line from my file.
是的。但是您不会将其写入控制台。看第三行,readln(x, s);
I am trying to read a file and randomly print it to console. I am wondering If I have to use arrays for that.
是的,这是一个合理的方法。
不使用记录数组,只需声明:
myArray : array of string;
要从数组中获取随机值,请使用 Randomize
初始化随机生成器,并使用 Random()
获取随机索引。
var
x: text;
myArray: array of String;
ix: Integer;
begin
Randomize; // Initiate the random generator
Assign(x, 'text.txt');
reset(x);
ix := 0;
SetLength(myArray, 0);
while not eof(x) do
begin
SetLength(myArray, Length(myArray) + 1);
readln(x, myArray[ix]);
WriteLn(myArray[ix]);
ix := ix + 1;
end;
WriteLn('Random line:');
WriteLn(myArray[Random(ix)]); // Random(ix) returns a random number 0..ix-1
end.
您的程序存在一些问题。
您的第一个 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 支持。
注意:以上所有片段都假定文件至少包含一行。如果不是这种情况,它们将失败,并且在 real/production 代码中,您 必须 验证这一点,例如喜欢
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
部分之前, 假定 array/list 不为空。
使用这个程序,我试图读取一个文件并将其随机打印到控制台。我想知道是否必须为此使用数组。例如,我可以将我的字符串分配到一个数组中,然后从我的数组中随机打印。但是,我不确定如何处理。另一个问题是,我当前的程序没有从我的文件中读取第一行。我有一个包含
的文本文件text.txt
1. ABC
2. ABC
...
6. ABC
下面是我的代码。
type
arr = record
end;
var
x: text;
s: string;
SpacePos: word;
myArray: array of arr;
i: byte;
begin
Assign(x, 'text.txt');
reset(x);
readln(x, s);
SetLength(myArray, 0);
while not eof(x) do
begin
SetLength(myArray, Length(myArray) + 1);
readln(x, s);
WriteLn(s);
end;
end.
请告诉我如何解决这个问题!
我在清楚地阅读您的问题后编辑了答案。
尽管如此,对于您阅读额外行的问题,您在进入阅读循环之前碰巧阅读了一行。所以这是你的程序从开始到结束的语句,没有额外的 readln()。
这段代码套路很简单,其实我能想到的方法不止一种。对于第一种方法,您可以将每一行读入一个数组。然后遍历数组并创建一个 0 或 1 的随机数。如果是 1,则打印该行。
对于第二种方法,每次从文件中读取一行,生成一个0或1的随机数。如果该随机数是1,则打印该行。
请注意在 运行 random() 之前使用 randomize,以免获得与上次程序执行相同的随机数。另一件需要考虑的事情是,如果你要处理一个大文本文件,每个设置的长度都会花费很多。最好跟踪那里发生的事情并将 20 - 30 长度设置为 1 甚至 100。如果你使用数组路由。
下面的代码是数组的方法,对于不使用数组的方法,看了下面的例程就很简单了
var
x: text;
SpacePos: word;
myArray: array of string;
i: integer;
begin
Randomize;
Assign(x, 'text.txt');
reset(x);
SetLength(myArray, 0);
i := 0;
while not eof(x) do
begin
SetLength(myArray, Length(myArray) + 1);
readln(x, myArray[i]);
i := i + 1;
end;
for i:= 0 to Length(myArray) - 1 do
begin
if random(2) = 1 then
WriteLn(myArray[i]);
end;
end.
Also another problem is that, my current program does not read the first line from my file.
是的。但是您不会将其写入控制台。看第三行,readln(x, s);
I am trying to read a file and randomly print it to console. I am wondering If I have to use arrays for that.
是的,这是一个合理的方法。
不使用记录数组,只需声明:
myArray : array of string;
要从数组中获取随机值,请使用 Randomize
初始化随机生成器,并使用 Random()
获取随机索引。
var
x: text;
myArray: array of String;
ix: Integer;
begin
Randomize; // Initiate the random generator
Assign(x, 'text.txt');
reset(x);
ix := 0;
SetLength(myArray, 0);
while not eof(x) do
begin
SetLength(myArray, Length(myArray) + 1);
readln(x, myArray[ix]);
WriteLn(myArray[ix]);
ix := ix + 1;
end;
WriteLn('Random line:');
WriteLn(myArray[Random(ix)]); // Random(ix) returns a random number 0..ix-1
end.
您的程序存在一些问题。
您的第一个
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 支持。
注意:以上所有片段都假定文件至少包含一行。如果不是这种情况,它们将失败,并且在 real/production 代码中,您 必须 验证这一点,例如喜欢
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
部分之前, 假定 array/list 不为空。