Файловый ввод-вывод в ada, как записать строки в файл?

У меня возникла проблема с записью строковой переменной в файл. Проблема в том, что я должен указать точную длину этой строки. В противном случае выходной файл будет просто содержать некоторые значения брака. Интересно, можно ли это как-то решить без необходимости заранее указывать длину строки?

Я знаю, что моя процедура Get() хранит длину этой переменной, и я просто могу вернуть ее в основную программу. Однако я хочу написать свою программу, чтобы она сначала выполняла все операции чтения из входного файла, прежде чем я начну запись в выходной файл.

with  Ada.Text_Io, Ada.Integer_Text_Io;
use Ada.Text_Io,Ada.Integer_Text_Io;

procedure Uppgift is

   type Bil_Register is
      record
     Namn    : String(1..50);
     Adress  : String(1..50);
     Post    : String(1..50);
     Reg     : String(1..6);
      end record;   

   Infil  : File_Type;
   Utfil        : File_Type;
   L, I : Integer;

   Br : Bil_Register;

   procedure Get(F : in out File_Type; Br : out Bil_Register) is
      Length : Integer;
   begin
      Get_Line(F, Br.Namn, Length);      
   end;

begin

   Open(Infil, In_File, "register.txt");
   Create(Utfil, Out_File, "test.txt");

   Get(Infil, Br);
   Put_Line(Utfil, Br.Namn);

   Close(Infil);
   Close(Utfil);

end Uppgift;

-

ИЗМЕНИТЬ (2011.08.20)

Похоже, это проблема только с ОС на базе Unix. При использовании Windows вам не нужно быть абсолютным с размером строки, когда вы печатаете его в файл или на экран.


person starcorn    schedule 26.06.2011    source источник


Ответы (3)


Что ж, длину действительной части строки нужно отслеживать где-то.

Вы можете поддерживать действительную длину каждого из строковых полей вашей записи в виде отдельного поля:

Namn        : String (1..50);
Namn_Length : Natural;

Вы можете определить свой собственный пакет типа переменной строки или использовать уже существующий, например Variable_Length. Например.

Namn : Variable_Length.Variable_String(50);

Вы можете использовать Unbounded_String для полей и переменных:

Namn : Unbounded_String;

И Ada.Text_IO.Unbounded_IO для ввода/вывода:

with Ada.Strings.Unbounded;
use Ada.Strings.Unbounded;
with Ada.Text_IO.Unbounded_IO;

procedure UTIO_Demo is

   use Ada.Text_IO;

   F    : Ada.Text_IO.File_Type;
   Data : Unbounded_String := To_Unbounded_String("Output by Unbounded_IO");

begin
   Create(F, Ada.Text_IO.Out_File, "utio.tst");

   Unbounded_IO.Put_Line(F, Data);

   Close(F);
end UTIO_Demo;

Если вы предпочитаете не использовать пакет Unbounded_IO, используйте To_String и To_Unbounded_String для преобразования туда и обратно между значениями Unbounded_String и строками, читаемыми и записываемыми через Text_IO.

person Marc C    schedule 26.06.2011
comment
у вас есть пример использования Unbounded_String для ввода-вывода с файлами? Я не могу заставить его работать. - person starcorn; 28.06.2011
comment
Я отредактировал вопрос с помощью быстрой демонстрации, которую я собрал вместе. Обратите внимание, что файл, с которым вы работаете, имеет тип файла Ada.Text_IO File_Type. - person Marc C; 28.06.2011

Лично я бы просто использовал Unbounded_String, как предложил Марк С, но если вы хотите избежать этого, вы можете сделать что-то вроде этого:

with Ada.Text_IO;
with Ada.Containers.Indefinite_Doubly_Linked_Lists;

use Ada.Text_IO;
use Ada.Containers;

procedure Uppgift is

   type Bil_Register (Namn_Length : Natural) is
      record
         Namn    : String (1 .. Namn_Length);
         --  Other components removed for brevity.
      end record;   

   package BR_Container is new Indefinite_Doubly_Linked_Lists (Bil_Register);
   use BR_Container;

   BR_List        : BR_Container.List;
   BR_List_Cursor : BR_Container.Cursor;
   Buffer         : String (1 .. 100);
   Length         : Natural;
   Register_File  : File_Type;
   Test_File      : File_Type;

begin

   --  First we read the contents of register.txt and add all the data to
   --  our list of Bil_Register objects.
   Open (File => Register_File,
         Mode => In_File,
         Name => "register.txt");

   while not End_Of_File (File => Register_File) loop
      Get_Line (File => Register_File,
                Item => Buffer,
                Last => Length);
      declare
         BR : Bil_Register 
           (Namn_Length => Length);
      begin
         BR.Namn := Buffer (1 .. Length);
         BR_List.Append (New_Item => BR);
      end;
   end loop;

   Close (File => Register_File);

   --  Then we output the contents of our list of Bil_Register objects to 
   --  test.txt
   Create (File => Test_File,
           Mode => Out_File,
           Name => "test.txt");

   BR_List_Cursor := BR_List.First;
   while Has_Element (Position => BR_List_Cursor) loop
      Put_Line (File => Test_File,
                Item => Element (Position => BR_List_Cursor).Namn);
      Next (Position => BR_List_Cursor);
   end loop;

   Close (File => Test_File); 

end Uppgift;

Я разделил чтение и запись на два блока, потому что вы сказали:

... сделать все чтение из входного файла, прежде чем я начну запись в выходной файл

Очевидно, что при использовании этого метода вам придется соответствующим образом изменить размер переменной Buffer. Но на самом деле это довольно неуклюже по сравнению с простым использованием Unbounded_String. Я бы сказал, что если у вас нет каких-то очень специфических проблем или требований, Unbounded_String, вероятно, подойдет. Это значительно упростит дело.

Удачи! :о)

person Thomas Løcke    schedule 27.06.2011

Как правило, вы можете обойтись без специальной переменной «длина» в Аде. К сожалению, это один из тех случаев, когда это очень сложно осуществить.

Однако есть трюк, который позволяет вам сделать это в этом случае. Если вы не возражаете против небольшой рекурсии и либо не ожидаете, что ваши строки будут чрезвычайно длинными, либо не слишком заботитесь о скорости выполнения (вы все равно выполняете ввод-вывод, поэтому он будет медленным) . Если это вас устраивает, попробуйте прием Карлайла.

function Next_Line(File : in Ada.Text_IO.File_Type :=
   Ada.Text_Io.Standard_Input) return String is
   Answer : String(1..256);
   Last   : Natural;
begin
   Ada.Text_IO.Get_Line(File => File,
      Item => Answer,
      Last => Last);
   if Last = Answer'Last then
      return Answer & Next_Line(File);
   else
      return Answer(1..Last);
   end if;
end Next_Line;

Теперь вы можете изменить свой код на:

begin

   Open(Infil, In_File, "register.txt");
   Create(Utfil, Out_File, "test.txt");

   Put_Line(Utfil, Next_Line (Infil));

   Close(Infil);
   Close(Utfil);

end Uppgift;
person T.E.D.    schedule 27.06.2011
comment
Я пытался использовать unbounded_string, однако я получаю некоторые жалобы на то, что я пытаюсь использовать его, но вместо этого он получил обычную строку. У вас случайно нет простого примера, как использовать его для чтения/записи файлов? - person starcorn; 28.06.2011