Ada 编程/库/Ada.Directories
此语言特性仅从 Ada 2005 开始可用。
Ada.Directories 是自 Ada 2005 以来 预定义语言环境 的一个单元。它允许对文件系统目录进行各种操作和查询。
如果环境支持文件和目录的概念,那么可以使用 Ada.Directories
以一种相当独立于操作系统的 방식 来操作它们。
这是一个高度依赖于实现的包,因此,可以从文件中提取的信息和关于目录的信息被保持在非常基本的水平。如果需要更多信息(所有者、组等),则应为此创建子包,如 Ada 参考手册 [1] 中的实现建议所述。
如果其他信息关于文件(如所有者或创建日期)在目录条目中可用,则实现应该在一个名为 Directories.Information 的子包中提供函数来检索它。
本节概述了 Ada.Directories
包的使用。如果还不够详细,你也可以参考 详细使用教程。
使用 Set_Directory
来更改当前工作目录。其余函数相当直白,除了 Containing_Directory
,它返回包含给定目录的目录的名称。
function
Current_Directoryreturn
String;procedure
Set_Directory (Directory :in
String);function
Exists (Name :in
String)return
Boolean;function
Containing_Directory (Name :in
String)return
String;
这个例子列出了当前工作目录中所有以 ".gpr" 结尾的条目。Start_Search
过程用于开始枚举目录。如果要删除筛选条件,可以传递一个空字符串。遍历目录涉及调用 Get_Next_Entry
来返回下一个目录条目,并调用 More_Entries
来检查是否有更多条目。搜索完成后,调用 End_Search
。
type
Search_Typeis
limited
private
;type
Directory_Entry_Typeis
limited
private
;procedure
Start_Search (Search :in
out
Search_Type; Directory :in
String; Pattern :in
String; Filter :in
Filter_Type := (others
=> True));procedure
Get_Next_Entry (Search :in
out
Search_Type; Directory_Entry :out
Directory_Entry_Type);function
More_Entries (Search :in
Search_Type)return
Boolean;procedure
End_Search (Search :in
out
Search_Type);
生成的 Directory_Entry_Type 包含完整的名称、简单名称、大小、类型和修改类型。
function
Simple_Name (Directory_Entry :in
Directory_Entry_Type)return
String;function
Full_Name (Directory_Entry :in
Directory_Entry_Type)return
String;function
Kind (Directory_Entry :in
Directory_Entry_Type)return
File_Kind; -- Directory, Ordinary_File, Special_Filefunction
Size (Directory_Entry :in
Directory_Entry_Type)return
File_Size;function
Modification_Time (Directory_Entry :in
Directory_Entry_Type)return
Ada.Calendar.Time;
with
Ada.Text_IO;with
Ada.Directories;use
Ada.Text_IO;use
Ada.Directories;procedure
Mainis
Dir : Directory_Entry_Type; Dir_Search : Search_Type; Curr_Dir : string := Current_Directory;begin
Put("Current Directory: "); Put_Line(Curr_Dir); Start_Search(Search => Dir_Search, Directory => Curr_Dir, Pattern => "*.gpr");loop
Get_Next_Entry(Dir_Search, Dir); Put(Full_Name(Dir)); Set_Col(50);if
Kind(Dir) = Ordinary_Filethen
Put(Size(Dir)'Image);end
if
; Set_Col(60); Put_Line(Kind(Dir)'Image);exit
when
not
More_Entries(Dir_Search);end
loop
; End_Search(Dir_Search);end
Main;
使用 Create_Directory
创建目录。Create_Path
创建路径中的所有目录。GNAT Ada 上的目录路径可以包含 "\" 或 "/" 字符。Rename
在目录中重命名目录或文件。<Delete_Tree> 删除整个目录和文件层次结构。Delete_Directory
删除空目录(非空目录抛出 Use_Error
异常)。Delete_File
删除目录中的普通文件。请注意,Form
参数是特定于实现的。在我使用的 GNAT Ada 版本中,它只能用于设置编码。
procedure
Create_Directory (New_Directory :in
String; Form :in
String := "");procedure
Create_Path (New_Directory :in
String; Form :in
String := "");procedure
Rename (Old_Name, New_Name :in
String);
procedure
Delete_Tree (Directory :in
String);procedure
Delete_Directory (Directory :in
String);procedure
Delete_File (Name :in
String);
以下是一个简单的例子,它创建一组目录并遍历这些目录。请注意,它在 Windows 和 Linux 上均可运行,使用 "/" 作为路径分隔符。返回的路径分隔符适合文件系统(例如,Windows 返回 "\")。
procedure
Mainis
Dir : Directory_Entry_Type; Dir_Search : Search_Type;begin
Start_Search(Search => Dir_Search, Directory => Curr_Dir, Pattern => ""); Create_Path("Foo"); Create_Path("Bar/Baz/Qux");loop
Get_Next_Entry(Dir_Search, Dir); Put_Line(Full_Name(Dir));exit
when
not
More_Entries(Dir_Search);end
loop
; End_Search(Dir_Search); Delete_Directory("Foo"); Delete_Tree("Bar");end
Main;
如果目录条目无效或枚举目录时超过最后一个目录,则会引发 Status_Error
。如果目录或文件名无法识别文件或目录(不存在或无效,具体取决于上下文),通常会引发 Name_Error
。如果目录不受支持或不可遍历,则会引发 Use_Error
。
Status_Error :exception
renames
Ada.IO_Exceptions.Status_Error; Name_Error :exception
renames
Ada.IO_Exceptions.Name_Error; Use_Error :exception
renames
Ada.IO_Exceptions.Use_Error; Device_Error :exception
renames
Ada.IO_Exceptions.Device_Error;
本文将基于一个名为 aDir
的小程序,随着我们逐步介绍各种函数、过程和类型,我们将向其中添加功能。在最基本的形式中,它所做的只是输出当前默认目录
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
end aDir;
运行上面的程序时,你应该看到类似于以下的输出
Starting default directory: /home/thomas/wiki_examples/aDir
你应该看到的是在你系统上启动 aDir
的目录的路径,而不是上面的路径(/home/thomas/wiki_examples/aDir
)。
在处理文件和目录时,需要一些基本的功能。我们必须能够移动到特定的目录;我们必须能够复制、重命名和删除文件和目录;我们必须能够创建新目录。幸运的是,这就是接下来的函数和过程使我们能够做的事情。
Current_Directory
的规范如下所示
function Current_Directory return String;
我们已经看到了 Current_Directory
如何工作,但关于 Current_Directory
还有一些值得一提的事情
Current_Directory
返回当前默认目录的完整目录名称。这并不等于程序本身所在的目录。- 返回的目录名称适用于 Set_Directory 过程。
- 如果操作系统/环境不支持默认目录,则会传播异常
Use_Error
。
当我们执行上面的基本aDir
程序时,输出的是aDir
程序在我的系统中所处的路径。但让我们看看如果我们从其他地方调用程序会发生什么。
$ pwd /home/thomas/wiki_examples/aDir $ ./adir Starting default directory: /home/thomas/wiki_examples/aDir $ cd && pwd /home/thomas $ wiki_examples/aDir/adir Starting default directory: /home/thomas
如您所见,Current_Directory
不等于实际程序的路径。相反,它返回当前默认目录,这完全依赖于实现。在上面的例子中,它根据从哪里调用程序而有所不同。因此,除非您使用 Set_Directory 特定地设置了默认目录,否则您无法 100% 确定 Current_Directory
将返回什么路径。
第二个要点是不言自明的,第三个要点我无法测试,因为我目前没有不支持目录概念的系统。
Ada.Directories.Current_Directory 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
end aDir;
Set_Directory
的规范如下所示
procedure Set_Directory (Directory : String);
使用 Set_Directory
,我们可以更改当前默认目录。将这段代码添加到 aDir
程序的主体中,以查看它的工作原理
D.Set_Directory (Directory => "/home/thomas");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);
如果我们现在执行程序,我们会得到以下结果
Starting default directory: /home/thomas/wiki_examples/aDir New default directory: /home/thomas
如果要设置的目录不存在,则会引发 Name_Error
异常。让我们看看它是如何工作的
D.Set_Directory (Directory => "/home/does/not/exist");
exception
when D.Name_Error =>
IO.Put_Line (Item => "Directory does not exist.");
如果您将上述代码片段添加到核心 aDir
程序中,您将获得类似于以下的输出
Starting default directory: /home/thomas/wiki_examples/aDir Directory does not exist.
由于 Set_Directory
设置的目录不存在,因此会引发并捕获 Name_Error
异常。但是,您不应该习惯性地使用 Set_Directory
来检查给定目录是否存在。对于这个简单的任务,我们有 Exists 函数。
Ada.Directories.Set_Directory 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Set_Directory (Directory => "/home/thomas");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);
end aDir;
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Set_Directory (Directory => "/home/does/not/exist");
exception
when D.Name_Error =>
IO.Put_Line (Item => "Directory does not exist.");
end aDir;
Create_Directory
的规范如下所示
procedure Create_Directory
(New_Directory : String;
Form : String := "");
使用此过程,我们可以(不出所料)创建新目录。但在尝试之前,我们将首先处理 Form
参数。RM 对此有以下说法
Form 参数可用于提供目录的系统相关特性;Form 参数的解释是实现定义的。Form 的空字符串指定使用新目录实现的默认选项。
对于我的特定环境(Slackware 12.1 和 GNATMAKE GPL 2008 编译器),Form
参数实际上没有任何作用。对于其他实现,情况可能并非如此,但我相当肯定,对于大多数标准系统(Unix、Linux、BSD、Windows),Form
未被使用。因此,Form
应该是一个空字符串,除非您在实现定义了 Form
用法的平台上。
但我们还是先谈谈这个——让我们在 aDir
中添加一些代码,看看 Create_Directory
是如何工作的
D.Create_Directory (New_Directory => "some_dir");
在程序中添加了这一行代码后,我们可以通过进行一些测试来查看程序是如何工作的
$ ls -l -rw-rw-rw- 1 thomas users 502 2009-09-24 22:09 Proj.gpr -rwxr-xr-x 1 thomas users 578199 2009-09-29 22:03 adir* -rw-rw-rw- 1 thomas users 517 2009-09-29 22:03 adir.adb drwxr-xr-x 2 thomas users 0 2009-09-29 22:03 objects/ $ ./adir Starting default directory: /home/thomas/wiki_examples/aDir $ ls -l -rw-rw-rw- 1 thomas users 502 2009-09-24 22:09 Proj.gpr -rwxr-xr-x 1 thomas users 578199 2009-09-29 22:03 adir* -rw-rw-rw- 1 thomas users 517 2009-09-29 22:03 adir.adb drwxr-xr-x 2 thomas users 0 2009-09-29 22:03 objects/ drwxr-xr-x 2 thomas users 0 2009-09-29 22:23 some_dir/ $ cd some_dir/ $ ../adir Starting default directory: /home/thomas/wiki_examples/aDir/some_dir $ ls -l drwxr-xr-x 2 thomas users 0 2009-09-29 22:25 some_dir/
从上面可以看出,Create_Directory
在当前默认目录中创建新目录,在本例中,该目录是我们调用 adir
的目录。如果我们想在当前默认目录以外的目录中创建 some_dir
目录,我们必须在 Create_Directory
行之前添加另一行代码
D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
当然,您应该将上面的路径替换为您系统中的路径。
接下来,我们删除已经创建的 some_dir
目录,然后尝试与之前相同的命令链
$ ls -l -rw-rw-rw- 1 thomas users 502 2009-09-24 22:09 Proj.gpr -rwxr-xr-x 1 thomas users 578224 2009-09-29 22:32 adir* -rw-rw-rw- 1 thomas users 635 2009-09-29 22:32 adir.adb drwxr-xr-x 2 thomas users 0 2009-09-29 22:32 objects/ $ ./adir Starting default directory: /home/thomas/wiki_examples/aDir $ ls -l -rw-rw-rw- 1 thomas users 502 2009-09-24 22:09 Proj.gpr -rwxr-xr-x 1 thomas users 578224 2009-09-29 22:32 adir* -rw-rw-rw- 1 thomas users 635 2009-09-29 22:32 adir.adb drwxr-xr-x 2 thomas users 0 2009-09-29 22:32 objects/ drwxr-xr-x 2 thomas users 0 2009-09-29 22:40 some_dir/ $ cd some_dir/ $ ../adir Starting default directory: /home/thomas/wiki_examples/aDir/some_dir raised ADA.IO_EXCEPTIONS.USE_ERROR : creation of new directory "some_dir" failed
我们的程序在第二次创建 some_dir
目录时失败,因为它已经存在,证明程序不再在我们调用程序的任何地方创建 some_dir
。这正是我们想要的,虽然我们可能应该添加一些代码来捕获异常,而不是让程序崩溃
exception
when D.Use_Error =>
IO.Put_Line ("Directory cannot be created.");
现在让我们再次尝试最后一个命令
$ ../adir Starting default directory: /home/thomas/wiki_examples/aDir/some_dir Directory cannot be created.
好多了!
除了 Use_Error
之外,还有 Name_Error
,如果给定的名称不是有效的目录,则会引发该错误。让我们尝试将 some_dir
更改为显然不能作为目录名称的东西:一个空字符串。我们只需要更改程序中的一行代码
D.Create_Directory (New_Directory => "some_dir");
到
D.Create_Directory (New_Directory => "");
现在,运行程序,我们会得到
Starting default directory: /home/thomas/wiki_examples/aDir/some_dir raised ADA.IO_EXCEPTIONS.NAME_ERROR : invalid new directory path name ""
我们可以通过添加另一个异常处理程序来捕获 Name_Error
exception
when D.Use_Error =>
IO.Put_Line (Item => "Directory cannot be created.");
when D.Name_Error =>
IO.Put_Line (Item => "Directory is not valid.");
运行程序,我们现在得到
Starting default directory: /home/thomas/wiki_examples/aDir Directory is not valid.
完美。
Ada.Directories.Create_Directory 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
D.Create_Directory (New_Directory => "some_dir");
-- Running the program multiple times will raise the Use_Error.
-- Changing some_dir to a null string will raise the Name_Error.
exception
when D.Use_Error =>
IO.Put_Line (Item => "Directory cannot be created.");
when D.Name_Error =>
IO.Put_Line (Item => "Directory is not valid.");
end aDir;
Delete_Directory
的规范如下所示
procedure Delete_Directory (Directory : String);
使用 Delete_Directory
,您可以删除一个空的目录,并且程序对其具有足够的权限。Directory
参数接受单个命名目录或目录的完整路径。对于单个名称,会将当前默认目录添加到前面,以创建一个完整路径。
将这些行添加到基本 aDir
程序中,以查看它的实际操作
D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Create_Directory (New_Directory => "some_dir");
if D.Exists (Name => "some_dir") then
IO.Put_Line ("some_dir exists.");
end if;
D.Delete_Directory (Directory => "some_dir");
D.Create_Directory (New_Directory => "some_dir2");
if D.Exists (Name => "some_dir2") then
IO.Put_Line ("some_dir2 exists.");
end if;
D.Delete_Directory (Directory => "/home/thomas/wiki_examples/aDir/some_dir2");
执行此程序,我们将获得以下输出
$ ./adir Starting default directory: /home/thomas/wiki_examples/aDir New default directory: /home/thomas/wiki_examples/aDir some_dir exists. some_dir2 exists. $ ls -l -rw-rw-rw- 1 thomas users 502 2009-09-24 22:09 Proj.gpr -rwxr-xr-x 1 thomas users 578224 2009-09-30 16:13 adir* -rw-rw-rw- 1 thomas users 973 2009-09-30 16:14 adir.adb drwxr-xr-x 2 thomas users 0 2009-09-30 16:13 objects/
该示例表明 Delete_Directory
确实可以使用目录的完整路径和仅使用目录名称。这两个 Exists
调用只是为了帮助直观地了解目录实际上是创建的。
如果 Directory
字符串不匹配现有目录,则会引发 Name_Error
异常;如果无法删除目录或目录不是空的,则会引发 Use_Error
异常。将此代码添加到核心 aDir
程序中
Delete_Non_Existing :
declare
begin
D.Delete_Directory (Directory => "does_not_exist");
exception
when D.Name_Error =>
IO.Put_Line (Item => "Directory not found.");
end Delete_Non_Existing;
Delete_With_Contents :
declare
begin
D.Delete_Directory (Directory => "dir_with_contents");
exception
when D.Use_Error =>
IO.Put_Line (Item => "Cannot delete non-empty directory.");
end Delete_With_Contents;
输出为
Starting default directory: /home/thomas/wiki_examples/aDir Directory not found. Cannot delete non-empty directory.
如预期的那样。要删除非空目录,您必须使用 Delete_Tree 过程。
Ada.Directories.Delete_Directory 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Set_Directory (Directory => "/home/thomas/wiki_examples/aDir");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Create_Directory (New_Directory => "some_dir");
if D.Exists (Name => "some_dir") then
IO.Put_Line ("some_dir exists.");
end if;
D.Delete_Directory (Directory => "some_dir");
D.Create_Directory (New_Directory => "some_dir2");
if D.Exists (Name => "some_dir2") then
IO.Put_Line ("some_dir2 exists.");
end if;
D.Delete_Directory (Directory => "/home/thomas/wiki_examples/aDir/some_dir2");
end Adir;
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
Delete_Non_Existing :
declare
begin
D.Delete_Directory (Directory => "does_not_exist");
exception
when D.Name_Error =>
IO.Put_Line (Item => "Directory not found.");
end Delete_Non_Existing;
Delete_With_Contents :
declare
begin
D.Delete_Directory (Directory => "dir_with_contents");
exception
when D.Use_Error =>
IO.Put_Line (Item => "Cannot delete non-empty directory.");
end Delete_With_Contents;
end aDir;
Create_Path
的规范如下所示
procedure Create_Path
(New_Directory : String;
Form : String := "");
使用 Create_Path
,可以创建嵌套目录,即使父目录不存在也是如此。这就像发出 mkdir -p /some/path/to/new/dir
命令,其中创建了整个路径,一直到 dir
目录。
与它的兄弟 Create_Directory
一样,Form
参数用于实现特定特性。空字符串指定使用实现创建新目录的默认选项。
要创建 this/is/a/new/directory
目录,我们只需在 aDir
程序中添加以下内容
D.Create_Path (New_Directory => "this/is/a/new/directory");
if D.Exists (Name => "this/is/a/new/directory") then
IO.Put_Line (Item => "this/is/a/new/directory exists!");
end if;
输出为
Starting default directory: /home/thomas/wiki_examples/aDir this/is/a/new/directory exists!
如果调用 Create_Path
时路径已经存在,则该过程什么也不做。
Create_Path
相对于当前默认目录工作,除非给定的 Directory
描述了从文件系统根目录开始的路径,例如,*nix 系统的 / 和 Windows 系统的 c:/。要创建 /tmp
中的 this/is/a/new/directory
,我们只需在 Directory
字符串中添加 /tmp
D.Create_Path (New_Directory => "/tmp/this/is/a/new/directory");
if D.Exists (Name => "/tmp/this/is/a/new/directory") then
IO.Put_Line (Item => "/tmp/this/is/a/new/directory exists!");
end if;
因此,如果路径是相对的,则当前默认目录用作基目录,但如果路径是绝对的,则忽略当前默认目录,并完全根据参数创建路径。
两个异常 Name_Error
和 Use_Error
在与 Create_Directory
相同的情况下引发:如果 New_Directory
字符串未标识目录,则引发 Name_Error
;如果程序无法创建任何一个目录,例如由于权限问题,则引发 Use_Error
。
Ada.Directories.Create_Path 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Create_Path (New_Directory => "this/is/a/new/directory");
if D.Exists (Name => "this/is/a/new/directory") then
IO.Put_Line (Item => "this/is/a/new/directory exists!");
end if;
end aDir;
Delete_Tree
的规范如下所示
procedure Delete_Tree (Directory : String);
Delete_Tree
使我们能够一举删除目录及其内容,因此,如果我们继续使用 Create_Path 中的示例,我们可以通过将以下内容添加到程序中来删除 this/is/a/new/directory
树
D.Delete_Tree (Directory => "this/");
if not D.Exists (Name => "this/") then
IO.Put_Line (Item => "this/is/a/new/directory does NOT exist!");
end if;
输出为
Starting default directory: /home/thomas/wiki_examples/aDir this/is/a/new/directory exists! this/is/a/new/directory does NOT exist!
两个异常 Name_Error
和 Use_Error
也对我们可用。它们按预期工作,如果 Directory
不存在,则会引发 Name_Error
;如果 Directory
因某种原因而无法删除,则会引发 Use_Error
。RM 中有一条关于此行为的重要说明
如果外部环境不支持使用给定名称删除目录或其内容的一部分(在没有 Name_Error 的情况下),则会传播 Use_Error 异常。如果传播了 Use_Error,则未指定是否删除了目录内容的一部分。
重要的是要特别注意最后一句话:未指定是否删除了目录内容的一部分。
这意味着可能会引发 Use_Error
,但这并不一定意味着目录及其内容 100% 完好无损。其中一部分很可能已被删除。
最后,应该提到的是,Delete_Tree
在当前默认目录中查找 Directory
,除非给定了绝对路径。
Ada.Directories.Delete_Tree 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Create_Path (New_Directory => "this/is/a/new/directory");
if D.Exists (Name => "this/is/a/new/directory") then
IO.Put_Line (Item => "this/is/a/new/directory exists!");
end if;
D.Delete_Tree (Directory => "this/");
if not D.Exists (Name => "this/") then
IO.Put_Line (Item => "this/is/a/new/directory does NOT exist!");
end if;
end aDir;
Delete_File
的规范如下所示
procedure Delete_File (Name : String);
Delete_File
的工作方式与 Delete_Directory 非常相似,只是它删除文件而不是目录。在 Ada.Directories
的上下文中,文件可以是普通文件或特殊文件
外部文件可分类为目录、特殊文件或普通文件。目录是外部文件,它是目标系统上文件的容器。特殊文件是外部文件,无法通过预定义的 Ada 输入输出包来创建或读取。不是特殊文件或目录的外部文件称为普通文件。
通常的异常也适用于 Delete_File
。
让我们创建一个新文件
$ touch some_file
现在将此添加到基本 aDir
程序中
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file exists");
end if;
D.Delete_File (Name => "some_file");
if not D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file does NOT exist");
end if;
输出为
Starting default directory: /home/thomas/wiki_examples/aDir some_file exists some_file does NOT exist
some_file
文件不见了。
以下是一个示例,其中我们(A)没有删除 `some_file` 文件的权限,并且(B)尝试删除一个不存在的文件。
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file exists");
end if;
declare
begin
D.Delete_File (Name => "some_file");
exception
when D.Use_Error =>
IO.Put_Line (Item => "Cannot delete some_file");
end;
declare
begin
D.Delete_File (Name => "some_wrong_filename");
exception
when D.Name_Error =>
IO.Put_Line (Item => "File does not exist");
end;
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file still exists");
end if;
输出应该像这样
Starting default directory: /home/thomas/wiki_examples/aDir some_file exists Cannot delete some_file File does not exist some_file still exists
至此,我们完成了对 `Delete_File` 的介绍。
Ada.Directories.Delete_File 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file exists");
end if;
D.Delete_File (Name => "some_file");
if not D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file does NOT exist");
end if;
end aDir;
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file exists");
end if;
declare
begin
D.Delete_File (Name => "some_file");
exception
when D.Use_Error =>
IO.Put_Line (Item => "Cannot delete some_file");
end;
declare
begin
D.Delete_File (Name => "some_wrong_filename");
exception
when D.Name_Error =>
IO.Put_Line (Item => "File does not exist");
end;
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file still exists");
end if;
end aDir;
`Rename` 的规范如下所示
procedure Rename (Old_Name, New_Name : String);
`Rename` 过程重命名目录和文件,前提是 `New_Name` 不存在。如果 `Old_Name` 不匹配目录或文件,则会引发 `Name_Error` 异常,如果无法重命名目录/文件,则会引发 `Use_Error` 异常。在以下示例中,我们将假设存在目录 `some_dir` 和文件 `some_file`。
首先,我们将向 `aDir` 程序的声明部分添加一个过程
procedure Do_We_Exist (A_Name : String) is
begin
if D.Exists (Name => A_Name) then
IO.Put_Line (Item => "Yes, " & A_Name & " exists");
else
IO.Put_Line (Item => "No, " & A_Name & " does not exist");
end if;
end Do_We_Exist;
此过程只是程序中的一点糖。它主要用于避免重复使用 `if` 块。
接下来,我们将向程序主体添加几行代码
Do_We_Exist (A_Name => "some_new_dir");
Do_We_Exist (A_Name => "some_new_file");
D.Rename (Old_Name => "some_dir",
New_Name => "some_new_dir");
D.Rename (Old_Name => "some_file",
New_Name => "some_new_file");
Do_We_Exist (A_Name => "some_new_dir");
Do_We_Exist (A_Name => "some_new_file");
D.Rename (Old_Name => "some_new_dir",
New_Name => "some_dir");
D.Rename (Old_Name => "some_new_file",
New_Name => "some_file");
此操作的输出为
Starting default directory: /home/thomas/wiki_examples/aDir No, some_new_dir does not exist No, some_new_file does not exist Yes, some_new_dir exists Yes, some_new_file exists
重命名文件和目录从未如此简单。
Ada.Directories.Rename 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
procedure Do_We_Exist (A_Name : String) is
begin
if D.Exists (Name => A_Name) then
IO.Put_Line (Item => "Yes, " & A_Name & " exists");
else
IO.Put_Line (Item => "No, " & A_Name & " does not exist");
end if;
end Do_We_Exist;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
Do_We_Exist (A_Name => "some_new_dir");
Do_We_Exist (A_Name => "some_new_file");
D.Rename (Old_Name => "some_dir",
New_Name => "some_new_dir");
D.Rename (Old_Name => "some_file",
New_Name => "some_new_file");
Do_We_Exist (A_Name => "some_new_dir");
Do_We_Exist (A_Name => "some_new_file");
D.Rename (Old_Name => "some_new_dir",
New_Name => "some_dir");
D.Rename (Old_Name => "some_new_file",
New_Name => "some_file");
end aDir;
`Copy_File` 的规范如下所示
procedure Copy_File
(Source_Name : String;
Target_Name : String;
Form : String := "");
`Copy_File` 将现有文件 `Source_Name` 的内容复制到名为 `Target_Name` 的新文件。结果是源文件的副本。`Copy_File` 不复制目录。仅复制普通文件。如前所述,`Form` 参数仅在实现需要时使用。以下是 RM 对 `Form` 的描述
Form 参数可用于指定结果外部文件的系统相关特性;Form 参数的解释由实现定义。
要将 `some_file` 复制到 `some_new_file`,我们只需将以下行添加到 `aDir` 程序的主体中即可
D.Copy_File (Source_Name => "some_file",
Target_Name => "some_new_file");
请注意,如果 `some_new_file` 已经存在,它将被覆盖。通常的异常 `Name_Error` 和 `Use_Error` 也适用于 `Copy_File`
declare
begin
D.Copy_File (Source_Name => "some_non_existant_file",
Target_Name => "");
exception
when D.Name_Error =>
IO.Put_Line (Item => "Source and/or Target Name_Error");
end;
declare
begin
D.Copy_File (Source_Name => "some_file",
Target_Name => "/root/illegal_file_location");
exception
when D.Use_Error =>
IO.Put_Line (Item => "Source and/or Target Use_Error");
end;
如果 `Source_Name` 或 `Target_Name` 未正确标识文件,则会引发 `Name_Error` 异常。如果操作失败,则会引发 `Use_Error` 异常;文件存在,但由于其他原因,无法复制。
Ada.Directories.Copy_File 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
D.Copy_File (Source_Name => "some_file",
Target_Name => "some_new_file");
end aDir;
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
declare
begin
D.Copy_File (Source_Name => "some_non_existant_file",
Target_Name => "");
exception
when D.Name_Error =>
IO.Put_Line (Item => "Source and/or Target Name_Error");
end;
declare
begin
D.Copy_File (Source_Name => "some_file",
Target_Name => "/root/illegal_file_location");
exception
when D.Use_Error =>
IO.Put_Line (Item => "Source and/or Target Use_Error");
end;
end aDir;
以下六个函数不与文件系统交互。它们只是操作字符串。有了这些函数,我们可以构建文件路径字符串、从路径中提取文件名、提取文件扩展名以及发现包含文件的目录名称。
`Full_Name` 的规范如下所示
function Full_Name (Name : String) return String;
`Full_Name` 返回给定 `Name` 的完整路径。`Name` 参数可以是单个文件名、文件的相对路径或绝对路径。`Full_Name` 不关心文件是否实际存在。它只关心返回正确的完整路径。RM 对此的描述如下
返回对应于 Name 指定的文件名的完整名称。如果作为 Name 给定的字符串不允许识别外部文件(包括目录和特殊文件),则会传播 Name_Error 异常。
当我第一次阅读这句话时,我认为这句话 **如果作为 Name 给定的字符串不允许识别外部文件,则会传播 Name_Error 异常** 意味着如果给定的 `Name` 参数无法解析为实际存在的文件,则会引发 `Name_Error` 异常。然而,事实并非如此。相反,这意味着如果 `Name` 格式错误,则会引发 `Name_Error` 异常。
让我们看看它是如何工作的
IO.Put_Line (Item => D.Full_Name (Name => "foo"));
IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
D.Set_Directory (Directory => "/tmp");
IO.Put_Line (Item => D.Full_Name (Name => "foo"));
IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
IO.Put_Line (Item => D.Full_Name (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name parameter is malformed");
以及生成的输出
Starting default directory: /home/thomas/wiki_examples/aDir /home/thomas/wiki_examples/aDir/foo /home/thomas/wiki_examples/aDir/foo/bar /home/thomas/stuff New default directory: /tmp /tmp/foo /tmp/foo/bar /home/thomas/stuff Name parameter is malformed
与所有其他依赖于当前默认目录的函数和过程一样,如果 `Name` 是相对路径,则将 `Name` 参数附加到当前默认目录,而如果 `Name` 是绝对路径,则会忽略当前默认目录。
Ada.Directories.Full_Name 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Full_Name (Name => "foo"));
IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
D.Set_Directory (Directory => "/tmp");
IO.Put ("New default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Full_Name (Name => "foo"));
IO.Put_Line (Item => D.Full_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Full_Name (Name => "/home/thomas/stuff"));
IO.Put_Line (Item => D.Full_Name (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name parameter is malformed");
end aDir;
`Simple_Name` 的规范如下所示
function Simple_Name (Name : String) return String;
在 `Full_Name` 返回文件的完整路径的情况下,`Simple_Name` 返回路径的简单名称组件。因此,对于 `Name` 参数 `/home/thomas/foo`,它将返回 `foo`。`Name_Error` 异常仅在 `Name` 格式错误时引发。让我们看一个示例
IO.Put_Line (Item => D.Simple_Name (Name => "foo"));
IO.Put_Line (Item => D.Simple_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Simple_Name (Name => "/home/thomas/stuff"));
IO.Put_Line (Item => D.Simple_Name (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name parameter is malformed");
end aDir;
输出为
Starting default directory: /home/thomas/wiki_examples/aDir foo bar stuff Name parameter is malformed
非常方便。
Ada.Directories.Simple_Name 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Simple_Name (Name => "foo"));
IO.Put_Line (Item => D.Simple_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Simple_Name (Name => "/home/thomas/stuff"));
IO.Put_Line (Item => D.Simple_Name (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name parameter is malformed");
end aDir;
`Containing_Directory` 的规范如下所示
function Containing_Directory (Name : String) return String;
`Containing_Directory` 删除给定 `Name` 路径的简单名称。让我们看看它是如何工作的
IO.Put_Line (Item => D.Containing_Directory (Name => "foo"));
IO.Put_Line (Item => D.Containing_Directory (Name => "foo/bar"));
IO.Put_Line (Item => D.Containing_Directory (Name => "/home/thomas/stuff"));
declare
begin
IO.Put_Line (Item => D.Containing_Directory (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end;
declare
begin
IO.Put_Line (Item => D.Containing_Directory (Name => "/"));
-- No parent directory
exception
when D.Use_Error =>
IO.Put_Line (Item => "Use_Error raised. No containing directory.");
end;
输出为
Starting default directory: /home/thomas/wiki_examples/aDir /home/thomas/wiki_examples/aDir foo /home/thomas Name_Error raised. Malformed Name. Use_Error raised. No containing directory.
如预期的那样。请注意,`Use_Error` 异常仅在给定的 `Name` 参数没有包含目录时引发。
Ada.Directories.Containing_Directory 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Containing_Directory (Name => "foo"));
IO.Put_Line (Item => D.Containing_Directory (Name => "foo/bar"));
IO.Put_Line (Item => D.Containing_Directory (Name => "/home/thomas/stuff"));
declare
begin
IO.Put_Line (Item => D.Containing_Directory (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end;
declare
begin
IO.Put_Line (Item => D.Containing_Directory (Name => "/"));
-- No parent directory
exception
when D.Use_Error =>
IO.Put_Line (Item => "Use_Error raised. No containing directory.");
end;
end aDir;
`Extension` 的规范如下所示
function Extension (Name : String) return String;
如果你想提取文件的扩展名,这个函数就是你需要的东西。以下是一个使用示例
IO.Put_Line (Item => D.Extension (Name => "foo.txt"));
IO.Put_Line (Item => D.Extension (Name => "foo/bar"));
IO.Put_Line (Item => D.Extension (Name => "/home/thomas/stuff.conf"));
IO.Put_Line (Item => D.Extension (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
以及输出
Starting default directory: /home/thomas/wiki_examples/aDir txt conf Name_Error raised. Malformed Name.
如你所见,如果没有扩展名(foo/bar 行),则返回空字符串。当 `Name` 参数格式错误时,会引发 `Name_Error` 异常。
Ada.Directories.Extension 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Extension (Name => "foo.txt"));
IO.Put_Line (Item => D.Extension (Name => "foo/bar"));
IO.Put_Line (Item => D.Extension (Name => "/home/thomas/stuff.conf"));
IO.Put_Line (Item => D.Extension (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end aDir;
`Base_Name` 的规范如下所示
function Base_Name (Name : String) return String;
我确信你已经猜到 `Base_Name` 的作用了,但还是让我解释一下示例代码
IO.Put_Line (Item => D.Base_Name (Name => "foo.txt"));
IO.Put_Line (Item => D.Base_Name (Name => ".secret"));
IO.Put_Line (Item => D.Base_Name (Name => ".secret.conf"));
IO.Put_Line (Item => D.Base_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Base_Name (Name => "/home/thomas/stuff.conf"));
IO.Put_Line (Item => D.Base_Name (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
输出
Starting default directory: /home/thomas/wiki_examples/aDir foo .secret bar stuff Name_Error raised. Malformed Name.
请注意关于 `.secret` 文件的“奇怪”行为。它没有返回完整的 `.secret` 字符串(实际上是文件的基名),我们只得到了一个空字符串。这可能被认为有点奇怪,但它符合 `Base_Name` 编程人员的意图。以下是来自实际 `Base_Name` 函数的注释
-- Look for the last dot in the file name and return the part of the
-- file name preceding this last dot. If the first dot is the first
-- character of the file name, the base name is the empty string.
所以我们从源头上得到了答案:如果 `Base_Name` 返回空字符串,你可能正在处理一个点文件,你将不得不手动解析它。因此,你不能期望 `Base_Name` 返回与例如标准 GNU 工具 `basename` 相同的结果。`Base_Name` 必须在多个平台上可靠地工作,因此作者必须决定一个通用的、可预测的、方法。对于点文件返回空字符串就是这样,可预测。
Ada.Directories.Base_Name 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Base_Name (Name => "foo.txt"));
IO.Put_Line (Item => D.Base_Name (Name => ".secret"));
IO.Put_Line (Item => D.Base_Name (Name => ".secret.conf"));
IO.Put_Line (Item => D.Base_Name (Name => "foo/bar"));
IO.Put_Line (Item => D.Base_Name (Name => "/home/thomas/stuff.conf"));
IO.Put_Line (Item => D.Base_Name (Name => ""));
-- Malformed Name parameter
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised. Malformed Name.");
end aDir;
`Compose` 的规范如下所示
function Compose
(Containing_Directory : String := "";
Name : String;
Extension : String := "") return String;
`Compose` 允许我们从目录路径、简单名称和扩展名构建字符串。会进行一些基本检查以确保生成的字符串在语法上是正确的。`Compose` 不关心生成的路径是否实际存在,它只关心它作为文件完整名称的有效性。
让我们看看它是如何工作的
IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
Name => "bar",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "",
Name => "bar",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
Name => "bar",
Extension => ""));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
Name => "bar.conf",
Extension => ""));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
Name => "",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "",
Name => "",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
Name => "",
Extension => ""));
-- Force Name_Error by omitting Name and Extension
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised.");
以上操作的输出为
Starting default directory: /home/thomas/wiki_examples/aDir foo/bar.conf bar.conf /foo/bar /foo/bar.conf /foo/.conf .conf Name_Error raised. Malformed Name.
这并不奇怪。
当发生以下情况时,会引发 `Name_Error` 异常
如果作为 Containing_Directory 给定的字符串不为空且不允许识别目录,或者如果作为 Extension 给定的字符串不为空且不是可能的扩展名,或者如果作为 Name 给定的字符串不是可能的简单名称(如果 Extension 为空)或基名(如果 Extension 不为空),则会传播 Name_Error 异常。
`Compose` 在构建指向文件的字符串路径时非常方便。它比你自己连接字符串要好得多。
Ada.Directories.Compose 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
Name => "bar",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "",
Name => "bar",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
Name => "bar",
Extension => ""));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
Name => "bar.conf",
Extension => ""));
IO.Put_Line (Item => D.Compose (Containing_Directory => "/foo",
Name => "",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "",
Name => "",
Extension => "conf"));
IO.Put_Line (Item => D.Compose (Containing_Directory => "foo/",
Name => "",
Extension => ""));
-- Force Name_Error by omitting Name and Extension
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised.");
end aDir;
以下函数的目的很明显。例如 `Exists`、`Kind`、`Size` 和 `Modification_Time` 这样的名称,应该很容易猜到这些函数的作用,因此,我们毫不犹豫地开始吧。
`Exists` 的规范如下所示
function Exists (Name : String) return Boolean;
如果你想知道文件或目录是否存在,你应该使用 `Exists`。我们已经在一些前面的示例中看到了 `Exists` 的实际应用,但让我们再看一个代码片段,其中 `Exists` 是主角
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file exists");
end if;
if D.Exists (Name => "/home/thomas/wiki_examples/aDir/some_dir") then
IO.Put_Line (Item => "/home/thomas/wiki_examples/aDir/some_dir exists");
end if;
if not D.Exists (Name => "nonexistant_file") then
IO.Put_Line (Item => "nonexistant_file does not exist");
end if;
if D.Exists (Name => "") then
IO.Put_Line (Item => "This is impossible!");
end if;
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised");
以及输出
Starting default directory: /home/thomas/wiki_examples/aDir some_file exists /home/thomas/wiki_examples/aDir/some_dir exists nonexistant_file does not exist Name_Error raised
如你所见,如果给定的是相对路径,则会考虑当前默认目录,如果给定的 `Name` 无效(既不是文件也不是目录),则会引发 `Name_Error` 异常,就像上面示例中的空字符串一样。除此之外,关于此函数就没有什么好说的了。
Ada.Directories.Exists 示例源代码
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
if D.Exists (Name => "some_file") then
IO.Put_Line (Item => "some_file exists");
end if;
if D.Exists (Name => "/home/thomas/wiki_examples/aDir/some_dir") then
IO.Put_Line (Item => "/home/thomas/wiki_examples/aDir/some_dir exists");
end if;
if not D.Exists (Name => "nonexistant_file") then
IO.Put_Line (Item => "nonexistant_file does not exist");
end if;
if D.Exists (Name => "") then
IO.Put_Line (Item => "This is impossible!");
end if;
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised");
end aDir;
Kind
的规范如下所示
function Kind (Name : String) return File_Kind;
在 Ada.Directories
中,文件/目录被分类为三种可能的“文件类型”:DIRECTORY
、SPECIAL_FILE
或 ORDINARY_FILE
。DIRECTORY
是一个包含其他文件的目录。SPECIAL_FILE
是一个不能被预定义的 Ada 输入输出包读取或创建的文件。一个既不是 DIRECTORY
也不是 SPECIAL_FILE
的文件就是 ORDINARY_FILE
。
让我们看看它是如何工作的。首先,我们必须在 aDir
程序的声明部分添加一行代码
package IOE is new Ada.Text_IO.Enumeration_IO (D.File_Kind);
然后是主体
IOE.Put (Item => D.Kind (Name => "some_file"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => "some_dir"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => "/dev/sda"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => ""));
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised");
以及输出
Starting default directory: /home/thomas/wiki_examples/aDir ORDINARY_FILE DIRECTORY SPECIAL_FILE Name_Error raised
Kind
遵循当前默认目录,就像 Ada.Directories
中的所有其他函数和过程一样。如果给定的 Name
参数无效,则会引发 Name_Error
。
{{collapsible box|title=Ada.Directories.Kind 示例源代码|collapsed=yes|content=
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
package IOE is new Ada.Text_IO.Enumeration_IO (D.File_Kind);
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IOE.Put (Item => D.Kind (Name => "some_file"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => "some_dir"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => "/dev/sda"));
IO.New_Line;
IOE.Put (Item => D.Kind (Name => ""));
exception
when D.Name_Error =>
IO.Put_Line (Item => "Name_Error raised");
end aDir;
Size
的规范如下所示
function Size (Name : String) return File_Size;
Size
返回的 File_Size
类型是一个整数,其范围为 0 .. Long_Long_Integer'Last
。也就是说,在大多数系统上,这是一个非常大的数字。需要注意的是,Size
的作用完全符合预期
IO.Put_Line (Item => D.Size (Name => "some_file")'Img);
IO.Put_Line (Item => D.Size (Name => "adir")'Img);
IO.Put_Line
(Item => "Max File_Size on this system: " & Long_Long_Integer'Last'Img);
结果是
Starting default directory: /home/thomas/wiki_examples/aDir 7 578580 Max File_Size on this system: 9223372036854775807
返回的大小是文件中包含的流元素数量,在本例中是 7 个和 578580 字节。数字 9223372036854775807 旨在向您展示 File_Size
类型可以处理的数字有多大。我认为我们很难找到能够处理如此大文件的操作系统或文件系统。请注意,Long_Long_Integer
是实现相关的:它可能在您的系统上完全不同。
通常的 Name_Error
异常适用于 Size
,并且如果文件大小不是 File_Size
类型,您还会发现一个 Constraint_Error
,即它低于 0 或高于 Long_Long_Integer'Last
。
{{collapsible box|title=Ada.Directories.Size 示例源代码|collapsed=yes|content=
with Ada.Text_IO;
with Ada.Directories;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => D.Size (Name => "some_file")'Img);
IO.Put_Line (Item => D.Size (Name => "adir")'Img);
IO.Put_Line
(Item => "Max File_Size on this system: " & Long_Long_Integer'Last'Img);
end aDir;
Modification_Time
的规范如下所示
function Modification_Time (Name : String) return Ada.Calendar.Time;
如果您需要知道文件上次修改的时间,此函数可以满足您的需求。如您所见,它返回一个 Ada.Calendar.Time
对象,因此为了实际查看输出,我们需要添加一个 with
子句和一行额外的代码到 aDir
的声明部分。首先是 with
子句
with Ada.Calendar.Formatting;
然后是声明
package ACF renames Ada.Calendar.Formatting;
最后是添加到主体中的代码
IO.Put_Line (Item => ACF.Image (D.Modification_Time (Name => "some_file")));
这段代码片段的输出是
Starting default directory: /home/thomas/wiki_examples/aDir 2009-10-06 14:10:04
如果文件无效,则会引发通常的 Name_Error
,如果环境不支持文件修改时间的概念,则会引发 Use_Error
。
{{collapsible box|title=Ada.Directories.Modification_Time 示例源代码|collapsed=yes|content=
with Ada.Text_IO;
with Ada.Directories;
with Ada.Calendar.Formatting;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
package ACF renames Ada.Calendar.Formatting;
begin
IO.Put ("Starting default directory: ");
IO.Put_Line (Item => D.Current_Directory);
IO.Put_Line (Item => ACF.Image (D.Modification_Time (Name => "some_file")));
end aDir;
使用以下类型和子程序可以搜索目录结构。在前面的部分中,我们并没有真正关注类型,因为它们在使用函数和过程时没有发挥积极作用,但对于搜索来说,情况有所不同:在这里,我们积极地使用类型来跟踪搜索的状态和搜索的结果。
可以使用活动迭代器或被动迭代器进行搜索。活动迭代器方法只是一个简单的循环,而被动迭代器方法使用指向子程序的访问,该子程序定义了对搜索的每个结果的操作。我们将展示两种方法的工作方式。
Directory_Entry_Type
的规范如下所示
type Directory_Entry_Type is limited private;
private
type Directory_Entry_Type is record
Is_Valid : Boolean := False;
Simple : Ada.Strings.Unbounded.Unbounded_String;
Full : Ada.Strings.Unbounded.Unbounded_String;
Kind : File_Kind := Ordinary_File;
end record;
Directory_Entry_Type
表示目录中的单个条目。创建 Directory_Entry_Type
的唯一方法是使用 Get_Next_Entry 过程。然后,可以使用适当的子程序(例如 Simple_Name、Kind 等)来使用这样的 Directory_Entry_Type
对象。从 Directory_Entry_Type
记录中可以明显地看出我们可以从中提取哪些信息。
Filter_Type
的规范如下所示
type Filter_Type is array (File_Kind) of Boolean;
Ada.Directories
中有三种不同的文件类型:Directory
、Ordinary_File
和 Special_File
。使用 Filter_Type
,您可以定义要搜索的这些文件类型中的哪一种
Filter : Filter_Type := (Ordinary_File => True,
Special_File => False,
Directory => True);
在这里,我们设置了一个过滤器,它忽略 Special_File
,并允许 Ordinary_File
和 Directory
。
Search_Type
的规范如下所示
type Search_Type is limited private;'
type Search_Data;
type Search_Ptr is access Search_Data;
private
type Search_Type is new Ada.Finalization.Controlled with record
Value : Search_Ptr;
end record;
Search_Type
包含搜索的状态。您可以使用 Start_Search 初始化 Search_Type
对象,使用 More_Entries 检查它,使用 Get_Next_Entry 读取它,并使用 End_Search 清理它。
Start_Search
的规范如下所示
procedure Start_Search
(Search : in out Search_Type;
Directory : String;
Pattern : String;
Filter : Filter_Type := (others => True));
所有与搜索相关的类型和子程序都高度依赖于彼此。因此,我认为将所有这些都包含在一个完整的程序中,将是最有意义的。这意味着我们将偏离通常的 aDir
程序,而是使用这个
with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
package IOI is new IO.Integer_IO (D.File_Size);
package ACF renames Ada.Calendar.Formatting;
A_Search : D.Search_Type;
Search_Item : D.Directory_Entry_Type;
Filter : constant D.Filter_Type := (D.Ordinary_File => True,
D.Special_File => False,
D.Directory => True);
begin
D.Start_Search (Search => A_Search,
Directory => D.Current_Directory,
Pattern => "",
Filter => Filter);
while D.More_Entries (Search => A_Search) loop
D.Get_Next_Entry (Search => A_Search,
Directory_Entry => Search_Item);
IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
IO.Set_Col (To => 25);
if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
IOI.Put (Item => D.Size (Directory_Entry => Search_Item),
Width => 1);
IO.Put (Item => " bytes");
else
IO.Put (Item => "dir");
end if;
IO.Set_Col (To => 45);
IO.Put (Item => ACF.Image
(D.Modification_Time (Directory_Entry => Search_Item)));
IO.Set_Col (To => 70);
IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
IO.New_Line;
end loop;
D.End_Search (Search => A_Search);
end aDir;
该程序在我的系统上运行时的输出为
/home/thomas/wiki_examples/aDir/adir . dir 2009-10-23 20:17:22 /home/thomas/wiki_examples/aDir/. .. dir 2009-09-24 20:07:57 /home/thomas/wiki_examples/aDir/.. objects dir 2009-10-27 20:56:32 /home/thomas/wiki_examples/aDir/objects Proj.gpr 502 bytes 2009-09-24 20:09:20 /home/thomas/wiki_examples/aDir/Proj.gpr adir 594193 bytes 2009-10-23 20:17:22 /home/thomas/wiki_examples/aDir/adir some_dir dir 2009-10-06 14:10:25 /home/thomas/wiki_examples/aDir/some_dir adir.adb 1781 bytes 2009-10-23 20:17:21 /home/thomas/wiki_examples/aDir/adir.adb some_file 7 bytes 2009-10-06 14:10:04 /home/thomas/wiki_examples/aDir/some_file some_new_file 7 bytes 2009-10-06 14:37:15 /home/thomas/wiki_examples/aDir/some_new_file
Start_Search
为搜索设置了舞台。它接受四个参数:Search
,它包含搜索的状态;Directory
,它是要搜索的目录;Pattern
,它是用于匹配文件名的模式;以及 Filter
,它定义了搜索返回的文件类型。
Pattern
值的解释是实现相关的,但空字符串等于“匹配所有内容”。很有可能简单的、众所周知的模式(例如 *.txt
)会产生预期的结果:所有扩展名为 .txt
的文件都将匹配。
在本例中,我们搜索当前目录中所有不是 Special_File
(Filter 被分配为 D.Special_File => False
)的内容(空 Pattern
字符串)。
上面的程序使用了一个活动迭代器:while
循环。如您所见,只要 D.More_Entries (Search => A_Search)
为 True
,此循环就会继续。当不再满足该条件时,循环结束,我们调用 End_Search
来清理 A_Search
对象。这基本上重置了它,因此它不再包含任何条目。
如果我们使用被动迭代器,我们可以避免必须管理这种基本的日常维护工作。使用被动迭代器时,程序如下所示
with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
package IOI is new IO.Integer_IO (D.File_Size);
package ACF renames Ada.Calendar.Formatting;
procedure Write_Search_Item (Search_Item : in D.Directory_Entry_Type) is
begin
IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
IO.Set_Col (To => 25);
if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
IOI.Put (Item => D.Size (Directory_Entry => Search_Item),
Width => 1);
IO.Put (Item => " bytes");
else
IO.Put (Item => "dir");
end if;
IO.Set_Col (To => 45);
IO.Put (Item => ACF.Image
(D.Modification_Time (Directory_Entry => Search_Item)));
IO.Set_Col (To => 70);
IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
IO.New_Line;
end Write_Search_Item;
Filter : constant D.Filter_Type := (D.Ordinary_File => True,
D.Special_File => False,
D.Directory => True);
begin
D.Search (Directory => D.Current_Directory,
Pattern => "",
Filter => Filter,
Process => Write_Search_Item'Access);
end aDir;
输出为
/home/thomas/wiki_examples/aDir/adir . dir 2009-10-23 20:17:22 /home/thomas/wiki_examples/aDir/. .. dir 2009-09-24 20:07:57 /home/thomas/wiki_examples/aDir/.. objects dir 2009-10-27 20:56:32 /home/thomas/wiki_examples/aDir/objects Proj.gpr 502 bytes 2009-09-24 20:09:20 /home/thomas/wiki_examples/aDir/Proj.gpr adir 594193 bytes 2009-10-23 20:17:22 /home/thomas/wiki_examples/aDir/adir some_dir dir 2009-10-06 14:10:25 /home/thomas/wiki_examples/aDir/some_dir adir.adb 1781 bytes 2009-10-23 20:17:21 /home/thomas/wiki_examples/aDir/adir.adb some_file 7 bytes 2009-10-06 14:10:04 /home/thomas/wiki_examples/aDir/some_file some_new_file 7 bytes 2009-10-06 14:37:15 /home/thomas/wiki_examples/aDir/some_new_file
使用被动迭代器,您将摆脱 Search_Type
和 Start_Search
-> More_Entries
-> Get_Next_Entry
-> End_Search
链。它更加干净,也更容易避免错误,因为您不必担心忘记最后的 End_Search
调用。不过,两种方法都有各自的优势,因此请根据具体情况选择最合适的方案。
Search
过程接受的参数与 Start_Search
几乎相同,除了我们使用 Process
而不是 Search
。Process
当然是访问要处理搜索返回的每个条目的子程序。这个子程序必须接受一个参数:Item : in Directory_Entry_Type
。
{{collapsible box|title=Ada.Directories.Start_Search 示例源代码|collapsed=yes|content=
with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
package IOI is new IO.Integer_IO (D.File_Size);
package ACF renames Ada.Calendar.Formatting;
A_Search : D.Search_Type;
Search_Item : D.Directory_Entry_Type;
Filter : constant D.Filter_Type := (D.Ordinary_File => True,
D.Special_File => False,
D.Directory => True);
begin
D.Start_Search (Search => A_Search,
Directory => D.Current_Directory,
Pattern => "",
Filter => Filter);
while D.More_Entries (Search => A_Search) loop
D.Get_Next_Entry (Search => A_Search,
Directory_Entry => Search_Item);
IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
IO.Set_Col (To => 25);
if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
IOI.Put (Item => D.Size (Directory_Entry => Search_Item),
Width => 1);
IO.Put (Item => " bytes");
else
IO.Put (Item => "dir");
end if;
IO.Set_Col (To => 45);
IO.Put (Item => ACF.Image
(D.Modification_Time (Directory_Entry => Search_Item)));
IO.Set_Col (To => 70);
IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
IO.New_Line;
end loop;
D.End_Search (Search => A_Search);
end Adir;
with Ada.Text_IO;
with Ada.Directories; use Ada.Directories;
with Ada.Calendar.Formatting;
procedure aDir is
package IO renames Ada.Text_IO;
package D renames Ada.Directories;
package IOI is new IO.Integer_IO (D.File_Size);
package ACF renames Ada.Calendar.Formatting;
procedure Write_Search_Item (Search_Item : in D.Directory_Entry_Type) is
begin
IO.Put (Item => D.Simple_Name (Directory_Entry => Search_Item));
IO.Set_Col (To => 25);
if D.Kind (Directory_Entry => Search_Item) = D.Ordinary_File then
IOI.Put (Item => D.Size (Directory_Entry => Search_Item),
Width => 1);
IO.Put (Item => " bytes");
else
IO.Put (Item => "dir");
end if;
IO.Set_Col (To => 45);
IO.Put (Item => ACF.Image
(D.Modification_Time (Directory_Entry => Search_Item)));
IO.Set_Col (To => 70);
IO.Put (Item => D.Full_Name (Directory_Entry => Search_Item));
IO.New_Line;
end Write_Search_Item;
Filter : constant D.Filter_Type := (D.Ordinary_File => True,
D.Special_File => False,
D.Directory => True);
begin
D.Search (Directory => D.Current_Directory,
Pattern => "",
Filter => Filter,
Process => Write_Search_Item'Access);
end aDir;
End_Search
的规范如下所示
procedure End_Search (Search : in out Search_Type);
End_Search
是一个清理过程。只有在您之前调用过 Start_Search
时才需要使用 End_Search
。它用于重置 Search_Type
,有效地清除所有搜索条目。
典型的用法如下所示
Start_Search(Search => A_Search, ...);
while More_Entries (Search => A_Search) loop
Get_Next_Entry (Search => A_Search, ...);
-- Do stuff
end loop;
End_Search (Search => A_Search);
实际的使用示例可以在 此处 找到。
More_Entries
的规范如下所示
function More_Entries (Search : Search_Type) return Boolean;
More_Entries
仅在之前调用过 Start_Search
时才相关。调用 More_Entries
会返回布尔值 True
,如果在调用 Get_Next_Entry
时还有更多条目可用。否则将返回布尔值 False
。有关实际的使用示例,请参见 Start_Search。
Get_Next_Entry
的规范如下所示
procedure Get_Next_Entry
(Search : in out Search_Type;
Directory_Entry : out Directory_Entry_Type);
我将直接引用 Ada 参考手册 中关于 Get_Next_Entry
的内容
返回与模式和过滤器匹配的 Search 描述的搜索的下一个 Directory_Entry。如果不再有匹配项,则会引发 Status_Error。实现定义了如果 Directory 的内容在 Search 对象有效时发生更改(例如,由另一个程序),此例程返回的结果是否会发生更改。如果外部环境不支持对 Search 代表的目录的持续搜索,则会传播 Use_Error 异常。
这在 Start_Search 部分中有说明。
`Simple_Name` 的规范如下所示
function Simple_Name (Directory_Entry : Directory_Entry_Type) return String;
此 Simple_Name
函数的功能与“常规” Simple_Name 函数完全相同,只是它接受 Directory_Entry_Type
而不是 String
作为 Directory_Entry
参数。有关示例,请参见 Start_Search。
`Full_Name` 的规范如下所示
function Full_Name (Directory_Entry : Directory_Entry_Type) return String;
此 Full_Name
函数的功能与“常规” Full_Name 函数完全相同,只是它接受 Directory_Entry_Type
而不是 String
作为 Directory_Entry
参数。有关示例,请参见 Start_Search。
Kind
的规范如下所示
function Kind (Directory_Entry : Directory_Entry_Type) return File_Kind;
此 Kind
函数的功能与“常规” Kind 函数完全相同,只是它接受 Directory_Entry_Type
而不是 String
作为 Directory_Entry
参数。有关示例,请参见 Start_Search。
Size
的规范如下所示
function Size (Directory_Entry : Directory_Entry_Type) return File_Size;
此 Size
函数的功能与“常规” Size 函数完全相同,只是它接受 Directory_Entry_Type
而不是 String
作为 Directory_Entry
参数。有关示例,请参见 Start_Search。
Modification_Time
的规范如下所示
function Modification_Time
(Directory_Entry : Directory_Entry_Type) return Ada.Calendar.Time;
此 Modification_Time
函数的功能与“常规” Modification_Time 函数完全相同,只是它接受 Directory_Entry_Type
而不是 String
作为 Directory_Entry
参数。有关示例,请参见 Start_Search。
-- Standard Ada library specification -- Copyright (c) 2003-2018 Maxim Reznik <[email protected]> -- Copyright (c) 2004-2016 AXE Consultants -- Copyright (c) 2004, 2005, 2006 Ada-Europe -- Copyright (c) 2000 The MITRE Corporation, Inc. -- Copyright (c) 1992, 1993, 1994, 1995 Intermetrics, Inc. -- SPDX-License-Identifier: BSD-3-Clause and LicenseRef-AdaReferenceManual -- -------------------------------------------------------------------------with
Ada.Calendar;with
Ada.IO_Exceptions;package
Ada.Directoriesis
-- Directory and file operations:function
Current_Directoryreturn
String;procedure
Set_Directory (Directory :in
String);procedure
Create_Directory (New_Directory :in
String; Form :in
String := "");procedure
Delete_Directory (Directory :in
String);procedure
Create_Path (New_Directory :in
String; Form :in
String := "");procedure
Delete_Tree (Directory :in
String);procedure
Delete_File (Name :in
String);procedure
Rename (Old_Name :in
String; New_Name :in
String);procedure
Copy_File (Source_Name :in
String; Target_Name :in
String; Form :in
String := ""); -- File and directory name operations:function
Full_Name (Name :in
String)return
String;function
Simple_Name (Name :in
String)return
String;function
Containing_Directory (Name :in
String)return
String;function
Extension (Name :in
String)return
String;function
Base_Name (Name :in
String)return
String;function
Compose (Containing_Directory :in
String := ""; Name :in
String; Extension :in
String := "")return
String; -- File and directory queries:type
File_Kindis
(Directory, Ordinary_File, Special_File);type
File_Sizeis
range
0 .. implementation_defined;function
Exists (Name :in
String)return
Boolean;function
Kind (Name :in
String)return
File_Kind;function
Size (Name :in
String)return
File_Size;function
Modification_Time (Name :in
String)return
Ada.Calendar.Time; -- Directory searching:type
Directory_Entry_Typeis
limited
private
;type
Filter_Typeis
array
(File_Kind)of
Boolean;type
Search_Typeis
limited
private
;procedure
Start_Search (Search :in
out
Search_Type; Directory :in
String; Pattern :in
String; Filter :in
Filter_Type := (others
=> True));procedure
End_Search (Search :in
out
Search_Type);function
More_Entries (Search :in
Search_Type)return
Boolean;procedure
Get_Next_Entry (Search :in
out
Search_Type; Directory_Entry :out
Directory_Entry_Type);procedure
Search (Directory :in
String; Pattern :in
String; Filter :in
Filter_Type := (others
=> True); Process :not
null
access
procedure
(Directory_Entry :in
Directory_Entry_Type)); -- Operations on Directory Entries:function
Simple_Name (Directory_Entry :in
Directory_Entry_Type)return
String;function
Full_Name (Directory_Entry :in
Directory_Entry_Type)return
String;function
Kind (Directory_Entry :in
Directory_Entry_Type)return
File_Kind;function
Size (Directory_Entry :in
Directory_Entry_Type)return
File_Size;function
Modification_Time (Directory_Entry :in
Directory_Entry_Type)return
Ada.Calendar.Time; Status_Error :exception
renames
Ada.IO_Exceptions.Status_Error; Name_Error :exception
renames
Ada.IO_Exceptions.Name_Error; Use_Error :exception
renames
Ada.IO_Exceptions.Use_Error; Device_Error :exception
renames
Ada.IO_Exceptions.Device_Error;private
pragma
Import (Ada, Directory_Entry_Type);pragma
Import (Ada, Search_Type);end
Ada.Directories;
外部示例
[编辑源代码]- 在以下位置搜索
Ada.Directories
的 **示例**: Rosetta Code,GitHub (gists),任何 Alire 包 或 本维基教科书。 - 在以下位置搜索与
Ada.Directories
相关的 **帖子**: Stack Overflow,comp.lang.ada 或 任何与 Ada 相关的页面。
FSF GNAT
- 规范: a-direct.ads
- 主体: a-direct.adb
drake