Rationale for Ada 2005: Predefined library

RUSTOP
BACKNEXT

ENG

4. Operational environment

@ Two new packages are added to Ada 2005 in order to aid communication with the operational environment. They are Ada.Environment_Variables and Ada.Directories.

@ The package Ada.Environment_Variables has the following specification

  1        package Ada.Environment_Variables is
  2                pragma Preelaborate (Environment_Variables);
  3                function Value (Name : String) return String;
  4                function Exists (Name : String) return Boolean;
  5                procedure Set (Name : in String; Value : in String);
  6                procedure Clear (Name : in String);
  7                procedure Clear;
  8                procedure Iterate (Process : not null access procedure (Name, Value : in String));
  9        end Ada.Environment_Variables;

@ This package provides access to environment variables by name. What this means and whether it is supported depends upon the implementation. But most operating systems have environment variables of some sort. And if not, the implementation is encouraged to simulate them.

@ The values of the variable are also implementation defined and so simply represented by strings.

@ The behaviour is straightforward. We might check to see if there is a variable with the name "Ada" and then read and print her value and set it to 2005 if it is not, thus

  1        if not Exists ("Ada") then
  2                raise Horror; -- quel dommage!
  3        end if;
  4        Put ("Current value of Ada is "); Put_Line (Value ("Ada"));
  5        if Value ("Ada") /= "2005" then
  6                Put_Line ("Revitalizing Ada now");
  7                Set ("Ada", "2005");
  8        end if;

@ The procedure Clear with a parameter deletes the variable concerned. Thus Clear ("Ada") eliminates her completely so that a subsequent call Exists ("Ada") will return False. Note that Set actually clears the variable concerned and then defines a new one with the given name and value. The procedure Clear without a parameter clears all variables.

@ We can iterate over the variables using the procedure Iterate. For example we can print out the current state by

  1        procedure Print_One (Name, Value : in String) is
  2        begin
  3                Put_Line (Name & "=" & Value);
  4        end Print_One;
  5        ...
  6        Iterate (Print_One'Access);

@ The procedure Print_One prints the name and value of the variable passed as parameters. We then pass an access to this procedure as a parameter to the procedure Iterate and Iterate then calls Print_One for each variable in turn.

@ Note that the slave procedure has both Name and Value as parameters. It might be thought that this was unnecessary since the user can always call the function Value. However, real operating systems can sometimes have several variables with the same name; providing two parameters ensures that the name/value pairs are correctly matched.

@ Attempting to misuse the environment package such as reading a variable that doesn't exist raises Constraint_Error or Program_Error.

@ There are big dangers of race conditions because the environment variables are really globally shared. Moreover, they might be shared with the operating system itself as well as programs written in other languages.

@ A particular point is that we must not call the procedures Set or Clear within a procedure passed as a parameter to Iterate.

@ The other environment package is Ada.Directories. Its specification is

  1        with Ada.IO_Exceptions;
  2        with Ada.Calendar;
  3        package Ada.Directories is
  4                -- Directory and file operations:
  5                function Current_Directory return String;
  6                procedure Set_Directory (Directory : in String);
  7                procedure Create_Directory (New_Directory : in String; Form : in String := "");
  8                procedure Delete_Directory (Directory : in String);
  9                procedure Create_Path (New_Directory : in String; Form : in String := "");
 10                procedure Delete_Tree (Directory : in String);
 11                procedure Delete_File (Name : in String);
 12                procedure Rename (Old_Name : in String; New_Name : in String);
 13                procedure Copy_File
 14                (       Source_Name : in String;
 15                        Target_Name : in String;
 16                        Form        : in String := "");
 17                -- File and directory name operations :
 18                function Full_Name (Name : String) return String;
 19                function Simple_Name (Name : String) return String;
 20                function Containing_Directory (Name : String) return String;
 21                function Extension (Name : String) return String;
 22                function Base_Name (Name : String) return String;
 23                function Compose
 24                (       Containing_Directory : String := "";
 25                        Name                 : String;
 26                        Extension            : String := "") return String;
 27                -- File and directory queries:
 28                type File_Kind is (Directory, Ordinary_File, Special_File);
 29                type File_Size is range 0 .. implementation_defined;
 30                function Exists (Name : String) return Boolean;
 31                function Kind (Name : String) return File_Kind;
 32                function Size (Name : String) return File_Size;
 33                function Modification_Time (Name : String) return Ada.Calendar.Time;
 34                -- Directory searching:
 35                type Directory_Entry_Type is limited private;
 36                type Filter_Type is array (File_Kind) of Boolean;
 37                type Search_Type is limited private;
 38                procedure Start_Search
 39                (       Search    : in out Search_Type;
 40                        Directory : in String;
 41                        Pattern   : in String;
 42                        Filter    : in Filter_Type := (others => True));
 43                procedure End_Search (Search : in out Search_Type);
 44                function More_Entries (Search : Search_Type) return Boolean;
 45                procedure Get_Next_Entry
 46                (       Search          : in out Search_Type;
 47                        Directory_Entry : out Directory_Entry_Type);
 48                procedure Search
 49                (       Directory : in String;
 50                        Pattern   : in String;
 51                        Filter    : in Filter_Type := (others => True);
 52                        Process   : not null access procedure
 53                                                (Directory_Entry : in Directory_Entry_Type));
 54                -- Operations on Directory Entries:
 55                function Simple_Name (Directory_Entry : Directory_Entry_Type) return String;
 56                function Full_Name (Directory_Entry : Directory_Entry_Type) return String;
 57                function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind;
 58                function Size (Directory_Entry : Directory_Entry_Type) return File_Size;
 59                function Modification_Time (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time;
 60                Status_Error : exception renames Ada.IO_Exceptions.Status_Error;
 61                Name_Error   : exception renames Ada.IO_Exceptions.Name_Error;
 62                Use_Error    : exception renames Ada.IO_Exceptions.Use_Error;
 63                Device_Error : exception renames Ada.IO_Exceptions.Device_Error;
 64        private
 65                -- Not specified by the language
 66        end Ada.Directories;

@ Most operating systems have some sort of tree-structured filing system. The general idea of this package is that it allows the manipulation of file and directory names as far as is possible in a unified manner which is not too dependent on the implementation and operating system.

@ Files are classified as directories, special files and ordinary files. Special files are things like devices on Windows and soft links on Unix; these cannot be created or read by the predefined Ada input– output packages.

@ Files and directories are identified by strings in the usual way. The interpretation is implementation defined.

@ The full name of a file is a string such as

  1        "c:\adastuff\rat\library.doc"

@ and the simple name is "library.doc" At least that is in good old DOS. In Windows XP it is something like

  1        "C:\Documents and Settings\john.JBI3\My Documents\adastuff\rat\library.doc"

@ For the sake of illustration we will continue with the simple DOS example. The current directory is that set by the "cd" command. So assuming we have done

  1        c:\>cd adastuff
  2        c:\adastuff>

@ then the function Current_Directory will return the string "c:\adastuff". The procedure Set_Directory sets the current default directory. The procedures Create_Directory and Delete_Directory create and delete a single directory. We can either give the full name or just the part starting from the current default. Thus

  1        Create_Directory ("c:\adastuff\history");
  2        Delete_Directory ("history");

@ will cancel out.

@ The procedure Create_Path creates several nested directories as necessary. Thus starting from the situation above, if we write

  1        Create_Path ("c:\adastuff\books\old");

@ then it will first create a directory "books" in "c:\adastuff" and then a directory "old" in "books". On the other hand if we wrote Create_Path ("c:\adastuff\rat"); then it would do nothing since the path already exists. The procedure Delete_Tree deletes a whole tree including subdirectories and files.

@ The procedures Delete_File, Rename and Copy_File behave as expected. Note in particular that Copy_File can be used to copy any file that could be copied using a normal input–output package such as Text_IO. For example, it is really tedious to use Text_IO to copy a file intact including all line and page terminators. It is a trivial matter using Copy_File.

@ Note also that the procedures Create_Directory, Create_Path and Copy_File have an optional Form parameter. Like similar parameters in the predefined input–output packages the meaning is implementation defined.

@ The next group of six functions, Full_Name, Simple_Name, Containing_Directory, Extension, Base_Name and Compose just manipulate strings representing file names and do not in any way interact with the actual external file system. Moreover, of these, only the behaviour of Full_Name depends upon the current directory.

@ The function Full_Name returns the full name of a file. Thus assuming the current directory is still "c:\adastuff" Full_Name ("rat\library.doc") returns "c:\adastuff\rat\library.doc" and Full_Name ("library.doc") returns "c:\adastuff\library.doc". The fact that such a file does not exist is irrelevant. We might be making up the name so that we can then create the file. If the string were malformed in some way (such as "66##77") so that the corresponding full name if returned would be nonsense then Name_Error is raised. But Name_Error is never raised just because the file does not exist.

@ On the other hand Simple_Name ("c:\adastuff\rat\library.doc") returns "library.doc" and not "rat\library.doc". We can also apply Simple_Name to a string that does not go back to the root. Thus Simple_Name ("rat\library.doc"); is allowed and also returns "library.doc".

@ The function Containing_Directory_Name removes the simple name part of the parameter. We can even write

  1        Containing_Directory_Name ("..\rat\library.doc")

@ and this returns "..\rat"; note that it also removes the separator "\".

@ The functions Extension and Base_Name return the corresponding parts of a file name thus

  1        Base_Name ("rat\library.doc") -- "library"
  2        Extension ("rat\library.doc") -- "doc"

@ Note that they can be applied to a simple name or to a full name or, as here, to something midway between.

@ The function Compose can be used to put the various bits together, thus

  1        Compose ("rat", "library", "doc")

@ returns "rat\library.doc". The default parameters enable bits to be omitted. In fact if the third parameter is omitted then the second parameter is treated as a simple name rather than a base name.

@ So we could equally write

  1        Compose ("rat","library.doc")

@ The next group of functions, Exists, Kind, Size and Modification_Time act on a file name (that is the name of a real external file) and return the obvious result. (The size is measured in stream elements – usually bytes.) Various types and subprograms are provided to support searching over a directory structure for entities with appropriate properties. This can be done in two ways, either as a loop under the direct control of the programmer (sometimes called an active iterator) or via an access to subprogram parameter (often called a passive iterator). We will look at the active iterator approach first.

@ The procedures Start_Search, End_Search and Get_Next_Entry and the function More_Entries control the search loop. The general pattern is

  1        Start_Search ( ... );
  2        while More_Entries ( ... ) loop
  3                Get_Next_Entry ( ... );
  4                ... -- do something with the entry found
  5        end loop;
  6        End_Search ( ... );

@ Three types are involved. The type Directory_Entry_Type is limited private and acts as a sort of handle to the entries found. Valid values of this type can only be created by a call of Get_Next_Entry whose second parameter is an out parameter of the type Directory_Entry_Type. The type Search_Type is also limited private and contains the state of the search. The type Filter_Type provides a simple means of identifying the kinds of file to be found. It has three components corresponding to the three values of the enumeration type File_Kind and is used by the procedure Start_Search.

@ Suppose we want to look for all ordinary files with extension "doc" in the directory "c:\adastuff\rat".

@ We could write

  1        Rat_Search : Search_Type;
  2        Item : Directory_Entry_Type;
  3        Filter : Filter_Type := (Ordinary_File => True, others => False);
  4        ...
  5        Start_Search (Rat_Search, "c:\adastuff\rat", "*.doc", Filter);
  6        while More_Entries (Rat_Search) loop
  7                Get_Next_Entry (Rat_Search, Item);
  8                ... -- do something with Item
  9        end loop;
 10        End_Search (Rat_Search);

@ The third parameter of Start_Search (which is "*.doc" in the above example) represents a pattern for matching names and thus provides further filtering of the search. The interpretation is implementation defined except that a null string means match everything. However, we would expect that writing "*.doc" would mean search only for files with the extension "doc".

@ The alternative mechanism using a passive iterator is as follows. We first declare a subprogram such as

  1        procedure Do_It (Item : in Directory_Entry_Type) is
  2        begin
  3                ... -- do something with item
  4        end Do_It;

@ and then declare a filter and call the procedure Search thus

  1        Filter : Filter_Type := (Ordinary_File => True, others => False);
  2        ...
  3        Search ("c:\adastuff\rat", "*.doc", Filter, Do_It'Access);

@ The parameters of Search are the same as those of Start_Search except that the first parameter of type Search_Type is omitted and a final parameter which identifies the procedure Do_It is added.

@ The variable Item which we declared in the active iterator is now the parameter Item of the procedure Do_It.

@ Each approach has its advantages. The passive iterator has the merit that we cannot make mistakes such as forget to call End_Search. But some find the active iterator easier to understand and it can be easier to use for parallel searches.

@ The final group of functions enables us to do useful things with the results of our search. Thus Simple_Name and Full_Name convert a value of Directory_Entry_Type to the corresponding simple or full file name. Having obtained the file name we can do everything we want but for convenience the functions Kind, Size and Modification_Time are provided which also directly take a parameter of Directory_Entry_Type.

@ So to complete this example we might print out a table of the files found giving their simple name, size and modification time. Using the active approach the loop might then become

  1        while More_Entries (Rat_Search) loop
  2                Get_Next_Entry (Rat_Search, Item);
  3                Put (Simple_Name (Item)); Set_Col (15);
  4                Put (Size (Item/1000)); Put (" KB"); Set_Col (25);
  5                Put_Line (Image (Modification_Time (Item)));
  6        end loop;

@ This might produce a table such as

  1        access.doc     152 KB 2005-04-05 09:03:10
  2        containers.doc 372 KB 2005-06-14 21:39:05
  3        general.doc    181 KB 2005-03-03 08:43:15
  4        intro.doc      173 KB 2004-11-25 15:52:20
  5        library.doc    149 KB 2005-04-08 13:50:05
  6        oop.doc        179 KB 2005-02-25 18:34:55
  7        structure.doc  151 KB 2005-04-05 09:09:25
  8        tasking.doc    174 KB 2005-03-31 11:16:40

@ Note that the function Image is from the package Ada.Calendar.Formatting discussed in the previous section.

@ Observe that the search is carried out on the directory given and does not look at subdirectories. If we want to do that then we can use the function Kind to identify subdirectories and then search recursively.

@ It has to be emphasized that the package Ada.Directories is very implementation dependent and indeed might not be supported by some implementations at all. Implementations are advised to provide any additional useful functions concerning retrieving other information about files (such as name of the owner or the original creation date) in a child package Ada.Directories.Information.

@ Finally, note that misuse of the various operations will raise one of the exceptions Status_Error, Name_Error, Use_Error or Device_Error from the package IO_Exceptions.

Rationale for Ada 2005: Predefined library

@ENGRUSTOPBACKNEXT

4. Операционная среда

@ Два новых пакета добавлены к Аде 2005 чтобы помочь коммуникации с операционной средой. Это - Ada.Environment_Variables и Ada.Directories.

@ Пакет Ada.Environment_Variables имеет следующую спецификацию:

  1        package Ada.Environment_Variables is
  2                pragma Preelaborate (Environment_Variables);
  3                function Value (Name : String) return String;
  4                function Exists (Name : String) return Boolean;
  5                procedure Set (Name : in String; Value : in String);
  6                procedure Clear (Name : in String);
  7                procedure Clear;
  8                procedure Iterate (Process : not null access procedure (Name, Value : in String));
  9        end Ada.Environment_Variables;

@ Этот пакет обеспечивает доступ к переменным среды по имени. Что они означают и поддерживаются ли вообще зависит от реализации. Но у большинства операционных систем есть переменные среды некоторого вида. В противном случае предполагается что реализация будет моделировать их сама.

@ Значения переменной - также зависит от реализации и просто представлено строками.

@ Поведение является прямым. Мы могли бы выяснить, если есть переменная с названием "Ada" и затем читать и печатать ее значение и устанавливать её в Аде 2005 примерно таким образом:

  1        if not Exists ("Ada") then
  2                raise Horror; -- quel dommage!
  3        end if;
  4        Put_Line ("Current value of Ada is " & Value ("Ada"));
  5        if Value ("Ada") /= "2005" then
  6                Put_Line ("Revitalizing Ada now");
  7                Set ("Ada", "2005");
  8        end if;

@ Процедура Clear с параметром удаляет соответствующую переменную. Таким образом, процедура Clear ("Ada") устраняет ее так что последующий вызов Exists ("Ada") выдаст False. Отметим, что процедура Set фактически очищает соответствующую переменную а затем определяет новую с именем и значением. Процедура Clear без параметра очищает все переменные.

@ Мы можем выполнить итерации по всем переменным, используя процедуру Iterate. Например мы можем распечатать их текущее состояние таким образом:

  1        procedure Print_One (Name, Value : in String) is
  2        begin
  3                Put_Line (Name & "=" & Value);
  4        end Print_One;
  5        ...
  6        Iterate (Print_One'Access);

@ Процедура Print_One печатает название и значение переменной переданной параметром. Тогда мы передаем ссылку на эту процедуру процедуре Iterate которая итеративно вызывает процедуру Print_One для каждой переменной среды в свою очередь.

@ Отметим, что у подчиненной процедуры есть и имя (Name) и значение (Value) передаваемые как параметры. Можно было бы посчитать это излишним, так как пользователь может всегда вызвать функцию Value. Однако, у реальных операционных систем может иногда быть несколько переменных с одним и тем же именем; использование двух параметров гарантирует, что пара название - значения правильно согласованы.

@ Попытка неправильно использовать пакет Ada.Environment_Variables например для чтение переменной, которой не существует вызывает исключение Constraint_Error или Program_Error.

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

@ Специфический пункт состоит в том, что мы не должны вызывать процедуры Set или Clear в процедуре Iterate.

@ Другой пакет - Ада.Directories. Его спецификация:

  1        with Ada.IO_Exceptions;
  2        with Ada.Calendar;
  3        package Ada.Directories is
  4                -- Directory and file operations:
  5                function Current_Directory return String;
  6                procedure Set_Directory (Directory : in String);
  7                procedure Create_Directory (New_Directory : in String; Form : in String := "");
  8                procedure Delete_Directory (Directory : in String);
  9                procedure Create_Path (New_Directory : in String; Form : in String := "");
 10                procedure Delete_Tree (Directory : in String);
 11                procedure Delete_File (Name : in String);
 12                procedure Rename (Old_Name : in String; New_Name : in String);
 13                procedure Copy_File
 14                (       Source_Name : in String;
 15                        Target_Name : in String;
 16                        Form        : in String := "");
 17                -- File and directory name operations :
 18                function Full_Name (Name : String) return String;
 19                function Simple_Name (Name : String) return String;
 20                function Containing_Directory (Name : String) return String;
 21                function Extension (Name : String) return String;
 22                function Base_Name (Name : String) return String;
 23                function Compose
 24                (       Containing_Directory : String := "";
 25                        Name                 : String;
 26                        Extension            : String := "") return String;
 27                -- File and directory queries:
 28                type File_Kind is (Directory, Ordinary_File, Special_File);
 29                type File_Size is range 0 .. implementation_defined;
 30                function Exists (Name : String) return Boolean;
 31                function Kind (Name : String) return File_Kind;
 32                function Size (Name : String) return File_Size;
 33                function Modification_Time (Name : String) return Ada.Calendar.Time;
 34                -- Directory searching:
 35                type Directory_Entry_Type is limited private;
 36                type Filter_Type is array (File_Kind) of Boolean;
 37                type Search_Type is limited private;
 38                procedure Start_Search
 39                (       Search    : in out Search_Type;
 40                        Directory : in String;
 41                        Pattern   : in String;
 42                        Filter    : in Filter_Type := (others => True));
 43                procedure End_Search (Search : in out Search_Type);
 44                function More_Entries (Search : Search_Type) return Boolean;
 45                procedure Get_Next_Entry
 46                (       Search          : in out Search_Type;
 47                        Directory_Entry : out Directory_Entry_Type);
 48                procedure Search
 49                (       Directory : in String;
 50                        Pattern   : in String;
 51                        Filter    : in Filter_Type := (others => True);
 52                        Process   : not null access procedure
 53                                                (Directory_Entry : in Directory_Entry_Type));
 54                -- Operations on Directory Entries:
 55                function Simple_Name (Directory_Entry : Directory_Entry_Type) return String;
 56                function Full_Name (Directory_Entry : Directory_Entry_Type) return String;
 57                function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind;
 58                function Size (Directory_Entry : Directory_Entry_Type) return File_Size;
 59                function Modification_Time (Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time;
 60                Status_Error : exception renames Ada.IO_Exceptions.Status_Error;
 61                Name_Error   : exception renames Ada.IO_Exceptions.Name_Error;
 62                Use_Error    : exception renames Ada.IO_Exceptions.Use_Error;
 63                Device_Error : exception renames Ada.IO_Exceptions.Device_Error;
 64        private
 65                -- Not specified by the language
 66        end Ada.Directories;

@ У большинства операционных систем файловая система имеет древовидную структуру. Основная идея этого пакета состоит в том, что он осуществляет манипуляцию с именами файлов и директорий в максимально обобщённом виде, который не слишком зависит от реализации и операционной системы.

@ Файлы классифицированы как каталоги, специальные файлы и обычные файлы. Специальные файлы - рассматриваются как устройства в Windows и мягкие ссылки в Unix; они не могут быть созданы или читаться предопределенными Ада - пакетами ввода - вывода.

@ Файлы и каталоги идентифицированы строками обычным способом. Интерпретация - определяется реализацией.

@ Полное имя файла - представляет собой строку вида:

  1        "c:\adastuff\rat\library.doc"

@ где простое имя - "library.doc". По крайней мере это выглядело так в старом добром DOS-е. В Windows XP это - может быть как:

  1        "C:\Documents and Settings\john.JBI3\My Documents\adastuff\rat\library.doc"

@ Ради иллюстрации рассмотрим простой пример DOS. Пусть текущий каталог установлен командой "cd":

  1        c:\>cd adastuff
  2        c:\adastuff>

@ тогда функция Current_Directory выдаст строку "c:\adastuff". Процедура Set_Directory устанавливает текущий каталог. Процедуры Create_Directory и Delete_Directory создают и удаляют единственный каталог. Мы можем или дать полное имя или только часть, начинающуюся с текущего значения по умолчанию. Следующая последовательность операторов:

  1        Create_Directory ("c:\adastuff\history");
  2        Delete_Directory ("history");

@ сначала создаст каталог, затем удалит его.

@ Процедура Create_Path создает несколько вложенных каталогов по мере необходимости. Если мы напишем:

  1        Create_Path ("c:\adastuff\books\old");

@ сначала создастся каталог "books" в "c:\adastuff" а затем каталог "old" в "books". С другой стороны, если мы напишем Create_Path ("c:\adastuff\rat"); тогда никакой реакции не будет, так как путь уже существует. Процедура Delete_Tree удаляет целое дерево, включая подкаталоги и файлы.

@ Процедуры Delete_File, Rename, и Copy_File ведут себя как и ожидается из названия. Отметим особенность процедуры Copy_File которая может использоваться для копирования любого файла который может быть скопирован, использованием нормального пакет ввода - вывода, такого как Text_IO. Например, весьма утомительно использовать Text_IO для копирования файла с сохранением всех символов редактирования строк и терминаторов страницы. Это - тривиальный вопрос при использовании функции Copy_File.

@ Отметим также, что у процедур Create_Directory, Create_Path и Copy_File есть дополнительный параметр Form. Как и подобные параметры в предопределенных пакетах ввода - вывода это значение определяется реализацией.

@ Следующая группа из шести функций Full_Name, Simple_Name, Containing_Directory, Extension, Base_Name и Compose выполняют манипуляции со строками представляющими имена файлов и ни в коей мере не взаимодействуют с фактической файловой системой. И только поведение функции Full_Name зависит от текущего каталога.

@ Функция Full_Name возвращает полное имя файла. Т.е. если у нас текущий каталог все еще "c:\adastuff" то функция Full_Name ("rat\library.doc") возвратит строку "c:\adastuff\rat\library.doc", а вызов Full_Name ("library.doc") возвратит "c:\adastuff\library.doc". Факт, что такой файл не существует хоть и является несоответствием, но если имя задано синтаксически правильно то никаких исключительных ситуаций не возникает. Если же имя неправильное (например такое "66 ## 77") то соответствующее полное имя не имеет смысла, в этом случае возбуждается исключение Name_Error.

@ С другой стороны вызов Simple_Name ("c:\adastuff\rat\library.doc") возвращает "library.doc" а не "rat\library.doc". Мы можем также применить Simple_Name к строке, которая не начинается с корня. Таким образом, вызов Simple_Name ("rat\library.doc"); разрешен и также возвращает "library.doc".

@ Функция Containing_Directory_Name удаляет простую именную часть из параметра. Мы можем даже написать:

  1        Containing_Directory_Name ("..\rat\library.doc")

@ в результате получим "..\rat"; при этом будет также удалён разделитель "\".

@ Функции Extension и Base_Name возвращают соответствующие части имени файла таким образом:

  1        Base_Name ("rat\library.doc") -- "library"
  2        Extension ("rat\library.doc") -- "doc"

@ Обратите внимание, что они могут быть применены к простому или к полному имени или, как здесь, на полпути между.

@ Функция Compose может использоваться для соединения различных частей имени файла:

  1        Compose ("rat", "library", "doc")

@ в результате получим "rat\library.doc". Заданные по умолчанию параметры позволяют некоторым частям быть опущенными. Фактически, если третий параметр опущен тогда, второй параметр обрабатывается как простое а не базовое имя.

@ Таким образом, мы могли бы также написать:

  1        Compose ("rat","library.doc")

@ Следующая группа функций Exists, Kind, Size и Modification_Time работают с именем файла (которое является именем реального существующего файла) и возвращают очевидный из названия результат. (Размер измеряется в потоковых элементах - обычно это байты). Различные типы и подпрограммы предоставлены для поддержки поиска в структуре каталогов для объектов с соответствующими свойствами. Это может быть сделано двумя способами, через цикл под прямым управлением программиста (иногда назваемый активной итерацией) или через доступ к параметру подпрограммы (часто назваемый пассивной итерацией). Сначала рассмотрим способ с активной итерацией.

@ Процедуры Start_Search, End_Search, Get_Next_Entry и функция More_Entries управляют циклом поиска. Общая схема такая:

  1        Start_Search ( ... );
  2        while More_Entries ( ... ) loop
  3                Get_Next_Entry ( ... );
  4                ... -- do something with the entry found
  5        end loop;
  6        End_Search ( ... );

@ Здесь используются три типа. Тип Directory_Entry_Type является ограниченым частным типом и действует как своего рода маркер к найденным входам. Действительное значение этого типа может быть создано только вызовом процедуры Get_Next_Entry, второй параметр Directory_Entry_Type - параметр типа in. Тип Search_Type также является ограниченным частным типом и содержит состояние поиска. Тип Filter_Type обеспечивает простое средство идентификации вида файла, который будет найден. Он имеет три компоненты соответствующие трем значениям File_Kind перечислимого типа, и используется процедурой Start_Search.

@ Предположим, что мы хотим искать все обычные файлы с расширением "doc" в каталоге "c:\adastuff\rat". Тогда мы можем написать:

  1        Rat_Search : Search_Type;
  2        Item : Directory_Entry_Type;
  3        Filter : Filter_Type := (Ordinary_File => True, others => False);
  4        ...
  5        Start_Search (Rat_Search, "c:\adastuff\rat", "*.doc", Filter);
  6        while More_Entries (Rat_Search) loop
  7                Get_Next_Entry (Rat_Search, Item);
  8                ... -- do something with Item
  9        end loop;
 10        End_Search (Rat_Search);

@ Третий параметр Start_Search (значение которого "*.doc" в вышеупомянутом примере) представляет собой шаблон для поиска. Её интерпретация определяется реализацией за исключением того, что пустая строка означает соответствие всему. Однако, мы ожидали бы, что написание "*.doc" будет означать поиск только файлов с расширением "doc".

@ Альтернативный механизм использующий пассивную итерацию такой. Сначала мы объявляем подпрограмму следующего вида:

  1        procedure Do_It (Item : in Directory_Entry_Type) is
  2        begin
  3                ... -- do something with item
  4        end Do_It;

@ после чего объявляем фильтр и вызываем процедуру Search следующим образом:

  1        Filter : Filter_Type := (Ordinary_File => True, others => False);
  2        ...
  3        Search ("c:\adastuff\rat", "*.doc", Filter, Do_It'Access);

@ Параметры процедуры Search такие же что и у Start_Search за исключением того, что первый параметр типа Search_Type опущен а последний параметр, который идентифицирует процедуру Do_It добавлен.

@ Переменная Item, которую мы объявили в активной итерации, является теперь параметром Item процедуры Do_It.

@ У каждого подхода есть свои преимущества. При пассивной итерации мы не можем сделать ошибку забыв вызвать End_Search. Но некоторые находят, что активная итерация проще для понимания и может быть легко использована для параллельных поисков.

@ Последняя группа функций позволяет нам сделать полезные вещи с результатами нашего поиска. Таким образом, Simple_Name и Full_Name преобразовывают значение Directory_Entry_Type соответственно в простое или полное имя файла. Получив имя файла, мы можем сделать все что мы хотим, но для удобства предоставлены функции Kind, Size и Modification_Time которые непосредственно берут параметр Directory_Entry_Type.

@ Завершая этот пример мы можем распечатать таблицу найденных файлов, печатая их простое имя, размер и время модификации. Используя активный подход мы можем написать:

  1        while More_Entries (Rat_Search) loop
  2                Get_Next_Entry (Rat_Search, Item);
  3                Put (Simple_Name (Item)); Set_Col (15);
  4                Put (Size (Item/1000)); Put (" KB"); Set_Col (25);
  5                Put_Line (Image (Modification_Time (Item)));
  6        end loop;

@ В результате получим нечто такое:

  1        access.doc     152 KB 2005-04-05 09:03:10
  2        containers.doc 372 KB 2005-06-14 21:39:05
  3        general.doc    181 KB 2005-03-03 08:43:15
  4        intro.doc      173 KB 2004-11-25 15:52:20
  5        library.doc    149 KB 2005-04-08 13:50:05
  6        oop.doc        179 KB 2005-02-25 18:34:55
  7        structure.doc  151 KB 2005-04-05 09:09:25
  8        tasking.doc    174 KB 2005-03-31 11:16:40

@ Отметим использование функции Image из пакета Ada.Calendar.Formatting обсужденного в предыдущей секции.

@ Обратите внимание, что поиск выполняется только в текущем каталоге без подкаталогов. Если же мы хотим рассматривать и подкаталоги тогда, мы можем использовать функцию Kind, чтобы идентифицировать подкаталоги и затем искать рекурсивно.

@ Нужно подчеркнуть что пакет Ada.Directories сильно зависит от реализации и в действительности может вообще не поддерживаться некоторыми реализациями вообще. Реализациям советуют обеспечить любые дополнительные полезные функции относительно получения другой информации о файлах (таких как имя владельца или оригинальной даты создания) в дочернем пакете Ada.Directories.Information.

@ В заключении отметим, что неправильное употребление различных операций возбуждает одно из исключений Status_Error, Name_Error, Use_Error или Device_Error из пакета IO_Exceptions.

@ ENG RUS

TOP BACK NEXT

2010-10-24 00:26:58

. .