Handling EOF partway through 128-char read in ada.sequential_io

Not sure the best forum to use to ask Ada programming questions. I’ve used reasonable effort so far to solve with Google search and various trials.

How do I find the number of bytes actually read into variable “buffer” (array (1…128) of char) through ada.sequential_io read if EOF occurs while reading that 128 byte block of data? I had expected some tick attribute might apply.

I gave the Ada Reference Manual a quick check to provide this answer.

If Ada.Sequential_IO is instantiated with such a type, it provides no mechanism to handle a partial read. Either instantiate it with a different type, use a different package, or handle this case differently.

I haven’t looked at the rm to see (on the road). Is there an option to override the Read attribute for the byte and array types? If so you could put a counter var in to keep track.

I don’t have experience using Sequential IO, but perhaps this example can give some ideas:

File:

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Text_IO.Text_Streams; use Ada.Text_IO.Text_Streams;
with Ada.Streams;

procedure jdoodle is

    type Array_Type is array (Positive range 1 .. 128) of Character with Pack, Size => 128*8;
    
    type Streamable_With_Count is record
        Value : Array_Type;
        Count : Natural := 0;
    end record;
    
    procedure Read(
       Stream : not null access Ada.Streams.Root_Stream_Type'Class;
       Item   : out Streamable_With_Count);
    
    for Streamable_With_Count'Read use Read;
    
    procedure Read(
       Stream : not null access Ada.Streams.Root_Stream_Type'Class;
       Item   : out Streamable_With_Count)
    is begin
        Item.Count := 0;
        for Index in Item.Value'Range loop
            Character'Read(Stream, Item.Value(Index));
            Item.Count := Item.Count + 1;
            Put_Line("Read: " & Item.Value(Index)'Image);
        end loop;
    end Read;
    
    v : Streamable_With_Count := (Value => (others => ' '), Count => 0);
    
begin
    Streamable_With_Count'Read(Stream(Standard_Input), v);
exception
    when others => 
        Put_Line("Read" & v.Count'Image & " Characters");
end jdoodle;

Standard Input:

abcdefg

Output:

Read: 'a'
Read: 'b'
Read: 'c'
Read: 'd'
Read: 'e'
Read: 'f'
Read: 'g'
Read: LF
Read 8 Characters

gcc -c jdoodle.adb
gnatbind -x jdoodle.ali
gnatlink jdoodle.ali -o jdoodle

Thank you, Jere, for the helpful response. I use a disk file as input and my open routine conflicts with the modified read. Your example uses the default stdin. I’ve so far not been able to resolve how to integrate a working open. I feel a bit like a ballet audience member watching a workshop on ballet technique, looks good put if I get on stage to dance I fall over in spectacular fashion :slight_smile:

I tried to structure my example so you could change input types. If you notice I take a stream of standard in, not standard in directly, so that could be replaced with a file stream for example (or a serial port stream, or any type of stream).

I just took standard in’s stream due to lack of actual input to test, but streams are a general base type and can be anything. You can change 'Stream(Standard_Input)' to any stream you like if that helps.

I would recommend using the Ada.Streams.Stream_IO package so you can use the `Read attribute to get your count. See the modified example that uses a file as input and tries to read 128 characters but stops at 10 due to input limitations:

jdoodle.adb

with Ada.Text_IO; use Ada.Text_IO;
with Ada.Streams.Stream_IO;  Use Ada.Streams.Stream_IO;
with Ada.Streams;

procedure jdoodle is

    type Array_Type is array (Positive range 1 .. 128) of Character with Pack, Size => 128*8;
    
    type Streamable_With_Count is record
        Value : Array_Type;
        Count : Natural := 0;  -- This variable isn't read from the file, only used in processing
    end record;
    
    procedure Read(
       Stream : not null access Ada.Streams.Root_Stream_Type'Class;
       Item   : out Streamable_With_Count);
    
    for Streamable_With_Count'Read use Read;
    
    -- The magic happens in this operation
    procedure Read(
       Stream : not null access Ada.Streams.Root_Stream_Type'Class;
       Item   : out Streamable_With_Count)
    is begin
        Item.Count := 0;
        for Index in Item.Value'Range loop
            Character'Read(Stream, Item.Value(Index));  -- Read individual values of the array
            Item.Count := Item.Count + 1;  -- an exception won't update the count
            Put_Line("Read: " & Item.Value(Index)'Image);
        end loop;
    end Read;
    
    package My_IO renames Ada.Streams.Stream_IO;
    
    File : My_IO.File_Type;
    
    v : Streamable_With_Count := (Value => (others => ' '), Count => 0);
    
begin
    My_IO.Open(File, My_IO.In_File, "/uploads/jdoodle.txt");
    Put_Line("Opened File");
    Streamable_With_Count'Read(Stream(File), v);  -- This calls the magic function
    
    My_IO.Close(File);
exception
    when others => 
        My_IO.Close(File);
        Put_Line("Read" & v.Count'Image & " Characters");
end jdoodle;

/uploads/jdoodle.txt

abcdefghij

Program output:

Opened File
Read: 'a'
Read: 'b'
Read: 'c'
Read: 'd'
Read: 'e'
Read: 'f'
Read: 'g'
Read: 'h'
Read: 'i'
Read: 'j'
Read 10 Characters

gcc -c jdoodle.adb
gnatbind -x jdoodle.ali
gnatlink jdoodle.ali -o jdoodle

Notice how my example didn’t change much other than adding a file variable, opening the file variable, getting the stream from it (instead of Standard_Input) and using that stream in the same code.

Hopefully this helps some?

Ada.Streams.Stream_IO package: The Package Streams.Stream_IO
I used jdoodle to test Ada code: Online Compiler and Editor/IDE for Java, C, C++, PHP, Python, Ruby, Perl - Code and Run Online