Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reading String from a text file into Array in Pascal

With this program, I am trying to read a file and randomly print it to console. I am wondering If I have to use arrays for that. For example, I could assign my strings into an array, and randomly print from my array. But, I'm not sure how to approach to that. Also another problem is that, my current program does not read the first line from my file. I have a text file text.txt that contains

1. ABC
2. ABC
...
6. ABC

And below is my code.

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.

Please let me know how I could approach this problem!

like image 350
Christian Avatar asked Jan 26 '26 03:01

Christian


2 Answers

There are a few issues with your program.

  1. Your first Readln reads the first line of the file into s, but you don't use this value at all. It is lost. The first time you do a Readln in the loop, you get the second line of the file (which you do print to the console using Writeln).

  2. Your arr record type is completely meaningless in this case (and in most cases), since it is a record without any members. It cannot store any data, because it has no members.

  3. In your loop, you expand the length of the array, one item at a time. But you don't set the new item's value to anything, so you do this in vain. (And, because of the previous point, there isn't any value to set in any case: the elements of the array are empty records that cannot contain any data.)

  4. Increasing the length of a dynamic array one item at a time is very bad practice, because it might cause a new heap allocation each time. The entire existing array might need to be copied to a new location in your computer's memory, every time.

  5. The contents of the loop seem to be trying to do two things: saving the current line in the array, and printing it to the console. I assume the latter is only for debugging?

  6. Old-style Pascal I/O (text, Assign, Reset) is obsolete. It is not thread-safe, possibly slow, handles Unicode badly, etc. It was used in the 90s, but shouldn't be used today. Instead, use the facilities provided by your RTL. (In Delphi, for instance, you can use TStringList, IOUtils.TFile.ReadAllLines, streams, etc.)


A partly fixed version of the code might look like this (still using old-school Pascal I/O and the inefficient array handling):

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.

If you want to fix the inefficient array issue, but still not use modern classes, allocate in chunks:

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.

But if you have access to modern classes, things get much easier. The following examples use the modern Delphi RTL:

The generic TList<T> handles efficient expansion automatically:

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.

But you could simply use a 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.

Or you could keep the array approach and use 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.

As you can see, the modern approaches are much more convenient (less code). They are also faster and give you Unicode support.


Note: All snippets above assume that the file contains at least a single line. They will fail if this is not the case, and in real/production code, you must verify this, e.g. like

  if Length(arr) = 0 then
    raise Exception.Create('Array is empty.');

or

  if List.Count = 0 then
    raise Exception.Create('List is empty.');

before the // Print strings randomly part, which assumes that the array/list isn't empty.

like image 147
Andreas Rejbrand Avatar answered Jan 27 '26 17:01

Andreas Rejbrand


Also another problem is that, my current program does not read the first line from my file.

Yes it does. But you don't write it to the console. See the third line, 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.

Yes that is a sound approach.

Instead of using an array of a record, just declare:

myArray : array of string;

To get a random value from the array, use Randomize to initialize the random generator, and Random() to get a random index.

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.
like image 33
LU RD Avatar answered Jan 27 '26 16:01

LU RD



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!