How to delete a line in a text file, iterating files like containers

Hi,
Is it possible to treat files text/sequential/direct files like containers, and use the [] or array syntax ? I just noticed that I don’t know how to delete a line in a text file… or insert one. I need to open a text file and both write and read from it. So Aad.Text_IO.File isn’t the right type. But Direct_IO would only work with Character, which would be unimaginably tedious. Stream_IO… I don’t know where to start. What I wish to do is delete duplicates lines above a certain length.


Ok, I can’t open the same file in Append_File and In_File, and Out_File straight up deletes everything immediately. I really don’t know what is the procedure to work in text files…

You cannot delete a line from a text file, except for the last line(s). With Unix-style files (which are the files today’s OSes provide), it is not possible to insert or delete data in the middle of a file.

If you want to treat a file as a collection of lines and be able to insert and delete lines you should read the file into memory as a vector of lines, do your insertions and deletions and write it back. Of course this only works if the file fits into memory

1 Like

I thought so. Yes ideally I would treat the file as a container, locate the number of the lines to remove, and do something like File := File(1…n°1) & File(n1+1…Last(File). I’m surprised there is no library to do that ?

Simple Components library has a persistent files abstraction for objects. Maybe it could be leveraged

You could also Get_Line a file into an indefinite_vector of strings

I’m going the vector route indeed, seems the most straightforward. But do I iterate from a certain index to another without a slice function ?
I don’t understand why there is no similar tool.

for Line_Number in Vector.First_Index..Vector.Last_Index loop
      if Element(Line_number,Container)'Length > 400 then
         for J of (declare Cursor: vp.Cursor Line_Number..Length(Vector) when 
   end loop;
end loop;

If I can do that I can contacenate slices of the container easily enough, eliminating duplicated lines. Damn, I expected these kinds of work to be well-trodden path already :sweat_smile:

Yeah it puzzles me too. I was hoping the ARG would opt to add in more functionality to vector (included operations to return arrays of data).

When I made my own unbounded string, I actually took the vector class, extended it and added slice functions myself. You can do something like:

package Line_Vectors is new Ada.Containers.Indefinite_Vectors(Positive, String);
type Vector is new Line_Vectors.Vector with null record;
function Slice(Self : Vector; First, Last : Positive) return Vector;

...

function Slice(Self : Vector; First, Last : Positive) return Vector is
begin
   return Result : Vector do
      for Index in First .. Last loop
         Result.Append(Self(Index));
      end loop;
   end return;
end Slice;

** untested, just typed up on the fly, but similar to what I did.

EDIT: Just found that there is a current issue up for it at the ARG github. MIght be worth adding input if you think there is any needed:

1 Like

Not knowing much, I already feel like the whole container thing is an fantastic mess…

To be fair, coming from the world of Ada83 and Ada95 where there were no standard containers. I was elated when they got added.

I’m more sad that Ada doesn’t support some basic container types for limited objects. I’ve had multiple cases where I wanted a searchable grouping of limited objects. A hashed map made a lot of sense, but in order to use one, I had to either make my own or make a map of access types to library level limited types (which gets extremely messy in a system when you want it scale and access types are always dangerous to use correctly). I usually end up rolling my own containers for them.

You seem to be hung up on concatenating slices as a way of deleting things. What’s wrong with the Delete procedure?

File.Delete (Index => N);

You can look at Lined.Buffer for an example of using a vector to hold and manipulate the contents of a text file.

I came down to this delete solution as well, but my implementation fails for now, its behavior is erratic, for some file it works other.
Let’s start with this:

pragma Ada_2022;
pragma Extensions_Allowed (On);
with Ada.strings.fixed, Ada.Text_IO, Ada.Containers.Indefinite_Vectors;
use Ada.Text_IO, Ada.strings.fixed;

procedure Main is
   package vp is new ada.containers.indefinite_vectors (Positive,String);
   package pvp is new ada.containers.indefinite_vectors (Positive,Positive);
   vector : vp.Vector;
   line_number_vector: pvp.vector;
   File1: File_Type;
begin
   Open (File1, In_File, "Essai.txt");
   vp.Reserve_Capacity (vector,7000);
      vector := [for A in 1..Positive(Line(File1)) => Get_line(File1)];
   Close(file1);
   for L1 in vp.First_Index(Vector)..vp.Last_Index(Vector) loop
      if vp.Element(vector,L1)'Length > 50 then
         for J in L1+1..vp.Last_Index(Vector) loop
            if vp.Element(Vector,J) = vp.Element(Vector,L1) then
               pvp.Append(line_number_vector,J);
            end if;
         end loop;
      end if;
    end loop;
end Main;

The for expression should be populating the vector with at least as many elements as there are lines in the file. And the capacity should be enough. But this expression and no other raises “raised CONSTRAINT_ERROR : Main.vp.Replace_Element: Index is out of range”

I thank you for the links but… may I remind you that I am a beginner ? pragmarc is way out of my league. I work on this problem because of a text file I need debugging, but realistically I shouldn’t be doing this yet :sweat_smile:

The File.Line function returns the current line number. You can’t use it as you do.
Furthermore, the vector is empty, you can’t assign it an aggregate.

You can replace

vector := [for A in 1..Positive(Line(File1)) => Get_line(File1)];

with

   while not File1.End_Of_File loop
      Vector.Append (Get_Line (File1));
   end loop;

I was wondering about that, but ended up deleting my post cause I was unsure at the time. I’m glad someone else questioned this.

I looked in the RM and the way it reads to me, the iteration expression part is evaluated first (and once):
http://www.ada-auth.org/standards/22rm/html/RM-4-5-8.html

So that matches what you say if I read it right.

Ah my mistake I should have read the RM with more attention. Of course it’s the current line number. Too bad I wanted to use the new container syntax…
Damn, it’s done. Not too complicated after all. Good exercise. Thanks guy !