在 Swift 中操作目录
了解如何编写 Swift 代码来操作文件系统,包括更改、创建和删除目录。
本课的目标是概述如何操作本地文件系统目录。涵盖的主题包括:识别用户的文档目录和临时目录、查找当前工作目录、创建、删除和重命名目录,以及获取目录内容的列表。在完成目录管理主题后,下一课将介绍文件处理。
FileManager、FileHandle 和 Data 类
Foundation 框架提供了三个在处理文件和目录时不可或缺的类:
- FileManager:FileManager 类可用于执行基本的文件和目录操作,例如创建、移动、读取和写入文件,以及读取和设置文件属性。此外,该类还提供了一些方法,用于(除其他任务外)识别当前工作目录、切换到新目录、创建目录,以及列出目录的内容。
- FileHandle:FileHandle 类用于对文件执行低级别操作,例如定位到文件中的特定位置,按指定字节数块读取和写入文件内容,以及向现有文件追加数据。
- Data:Data 类提供了一个有用的存储缓冲区,可用于读取文件内容,或从动态存储的数据写入文件。
所有这些类都属于 Foundation 框架,因此在处理文件和目录时必须导入该框架:
1import Foundation
理解 Swift 中的路径名
Swift 使用标准的 UNIX 命名约定来定义路径名。因此,路径的每个组成部分之间用正斜杠(/)分隔。路径名开头的 / 指的是文件系统的根目录。
以 / 开头的路径称为绝对路径,因为它们相对于根目录指定了文件系统的位置。例如,/home/demo 是一个绝对路径。
不以斜杠开头的路径则被解释为相对于当前工作目录。例如,如果当前工作目录是 /home/demo,而路径名为 mapdata/local.xml,那么该文件的完整且绝对路径等价于 /home/demo/mapdata/local.xml。
获取默认 FileManager 对象的引用
FileManager 类包含一个名为 default 的属性,用于获取应用程序默认文件管理器实例的引用:
1let filemgr = FileManager.default
获取对象引用后,即可开始使用它来操作文件和目录。
识别当前工作目录
当应用程序首次加载时,它将在当前工作目录中运行。该位置取决于操作系统以及应用程序的启动方式。iOS 应用程序的工作目录会被设置为用户的根目录(/)。而从 Linux 命令行运行的 Swift 程序,则会被设置为程序启动时所在的目录。
可以通过访问文件管理器对象的 currentDirectoryPath 属性随时识别当前工作目录。例如,以下代码片段用于识别当前工作目录:
1import Foundation 2 3let filemgr = FileManager.default 4let currentPath = filemgr.currentDirectoryPath 5 6print("当前目录 = \(currentPath)")
识别文档目录
大多数操作系统都有“文档”目录的概念。例如,在 macOS 上,它可能位于 /Users/<username>/Documents,而在 Ubuntu Linux 上,它可能位于 /home/<username>/Documents。
在 iOS 上,设备上的每个应用都有自己的私有文档目录和临时目录,允许其读写数据。实际上,确切位置还会因应用运行在真实 iPhone 或 iPad 设备上,还是在 iOS 模拟器中而有所不同。
鉴于这些差异,唯一可靠地找到用户文档目录路径的方法是向 Swift 文件管理器查询。
可以通过调用文件管理器的方法 urls(for:) 来获取应用程序的文档目录,传入参数 .documentDirectory 表示需要文档目录的路径。.userDomainMask 参数表示我们正在查找位于应用程序主目录中的文档目录。该方法返回一个数组形式的结果对象,因此我们可以如下获取当前应用程序文档目录的路径:
1let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask) 2 3let docsDir = dirPaths[0].path 4 5print("文档目录路径 = \(docsDir)")
识别临时目录
除了文档目录外,大多数操作系统还提供一个临时目录,用于临时文件存储。当前应用程序的临时目录路径可通过调用 NSTemporaryDirectory C 函数获得:
1let tmpDir = NSTemporaryDirectory() 2 3print("临时目录 = \(tmpDir)")
更改目录
运行中的应用程序的当前工作目录可以通过调用 FileManager 实例的 changeCurrentDirectoryPath 方法进行更改。目标目录路径作为参数以字符串对象的形式传递给该实例方法。请注意,此方法返回布尔值 true 或 false,表示请求的目录更改是否成功。返回 false 通常表示指定的目录不存在,或应用程序缺少相应的访问权限:
1let tmpDir = NSTemporaryDirectory() 2 3print("更改前的当前目录 = \(filemgr.currentDirectoryPath)") 4 5if filemgr.changeCurrentDirectoryPath(tmpDir) { 6 print("更改后的当前目录 = \(filemgr.currentDirectoryPath)") 7} else { 8 print("无法更改为目录 \(tmpDir)") 9}
在上述示例中,首先获取用户临时目录的路径,然后将其作为参数传递给文件管理器对象的 changeCurrentDirectoryPath 方法,从而将当前工作目录更改为该位置。
列出目录内容
可以使用 contentsOfDirectory(atPath:) 方法获取指定目录中包含的文件列表。该方法接收目录路径作为参数,并返回一个数组对象,其中包含该目录中的文件和子目录名称。以下示例获取 /SampleCode 目录的内容列表并逐项显示:
1do { 2 let filelist = try filemgr.contentsOfDirectory(atPath: "/SampleCode") 3 4 for filename in filelist { 5 print(filename) 6 } 7} catch let error { 8 print("错误: \(error.localizedDescription)") 9}
创建新目录
在 Swift 中,使用 FileManager 类的 createDirectory(atPath:) 实例方法创建新目录。该方法以新目录的路径作为第一个参数,创建失败时会抛出错误。该方法的第二个参数定义是否自动创建任何中间目录层级。例如,如果我们希望创建路径为 /tmp/mydata/maps 的目录,而 mydata 子目录尚不存在,将 withIntermediateDirectories 参数设为 true 将会自动创建该目录,然后再在其内部创建 maps 子目录。如果此参数设为 false,则创建尝试将失败,因为 mydata 不存在,且我们未授权自动创建它。
该方法还接受一组属性作为额外参数,用于新目录。指定 nil 将使用默认属性。
以下代码片段在 /SampleCode 目录中创建一个名为 DATA 的新子目录:
1let newDir = "/SampleCode/DATA" 2 3do { 4 try filemgr.createDirectory(atPath: newDir, 5 withIntermediateDirectories: true, attributes: nil) 6} catch let error as NSError { 7 print("错误: \(error.localizedDescription)") 8} 9 10// 输出目录内容以验证 DATA 子目录的创建 11 12do { 13 let filelist = try filemgr.contentsOfDirectory(atPath: "/SampleCode") 14 15 for filename in filelist { 16 print(filename) 17 } 18} catch let error { 19 print("错误: \(error.localizedDescription)") 20}
删除目录
可以使用 removeItem(atPath:) 方法从文件系统中删除一个已存在的目录,将要删除的目录路径作为参数传递。例如,要删除前面示例中创建的 DATA 目录,可以编写如下代码:
1do { 2 try filemgr.removeItem(atPath: newDir) 3} catch let error { 4 print("错误: \(error.localizedDescription)") 5} 6 7// 输出目录内容以验证 DATA 子目录的删除 8 9do { 10 let filelist = try filemgr.contentsOfDirectory(atPath: "/SampleCode") 11 12 for filename in filelist { 13 print(filename) 14 } 15} catch let error { 16 print("错误: \(error.localizedDescription)") 17}
课程小结
-
Swift 中的文件和目录处理涉及使用
FileManager、FileHandle和Data类。 -
在操作文件和目录之前,必须获取默认
FileManager实例的引用。 -
FileManager可用于识别用户的临时目录和文档目录。 -
FileManager实例还可用于更改目录、创建和删除目录,以及生成目录内容列表。
测验
通过完成以下测验,测试你对 Swift 中文件系统目录操作的掌握程度。
练习
完成以下练习,将你对目录处理的知识付诸实践。
练习 1
修复以下代码中的编译错误:
练习
1let filemgr = FileManager.default
解答
1import Foundation // FileManager 类属于 Foundation 框架 2 3let filemgr = FileManager.default
练习 2
在以下示例中添加代码,以识别并显示当前工作目录:
练习
1import Foundation 2 3let filemgr = FileManager.default
解答
1import Foundation 2 3let filemgr = FileManager.default 4 5print("当前工作目录 = \(filemgr.currentDirectoryPath)")
练习 3
以下代码试图在一个尚未存在 /departments 目录的文件系统上创建路径为 /departments/accounts 的新子目录。
当前实现下,该代码无法成功创建子目录。请修改代码,使其能够成功创建:
练习
1import Foundation 2 3let filemgr = FileManager.default 4 5let newDir = "/departments/accounts" 6 7do { 8 try filemgr.createDirectory(atPath: newDir, 9 withIntermediateDirectories: false, attributes: nil) 10 } catch let error as NSError { 11 print("错误: \(error.localizedDescription)") 12}
解答
1import Foundation 2 3let filemgr = FileManager.default 4 5let newDir = "/departments/accounts" 6 7do { 8 // 由于 /departments 目录尚不存在,必须将 withIntermediateDirectories 参数设为 true 9 try filemgr.createDirectory(atPath: newDir, 10 withIntermediateDirectories: true, attributes: nil) 11 } catch let error as NSError { 12 print("错误: \(error.localizedDescription)") 13}
Quiz
路径 "data/files/xml" 属于以下哪种路径?