文章目录
  1. 1. 使用字符串方法读写文件
  2. 2. 使用 Foundation 文件处理类读写文件
  3. 3. 使用标准IO库读写文件

后台服务经常会用到文件读写操作。比如:

  • 读取配置文件
  • 写日志到磁盘

在 Swift 3.0 中,我们该如何进行文件读写操作呢?Swift 字符串提供了方法对文件进行读写,Swift Foundation 库提供了文件读写功能,如果你熟悉标准IO库,也可以简单封装一下,实现文件的基本读写操作。

使用字符串方法读写文件

Swift 3.0 可以直接加载文本文件内容到字符串中,也可以直接把字符串写入到文件当中:

1
2
3
4
5
6
7
8
9
10
11
12
let filePath = "./data.txt"
let writeString = "春江水暖鸭先知"
do {
try writeString.write(toFile: filePath, atomically: false, encoding: String.Encoding.utf8)
} catch let error as NSError {
print("Failed writing to: \(filePath), Error: " + error.localizedDescription)
}
let fileContents = try? String(contentsOfFile: filePath, encoding: String.Encoding.utf8)
print(fileContents) // 输出字符串可选类型

write方法是从NSString中变换过来的,Swift 3.0 把方法变得更加 Swift 化了。具体的方法定义可以查看官方文档。这里需要注意的是第二个参数atomically,类型是Bool,表示是否需要辅助文件,值为true时表示需要辅助文件,字符串先写入到辅助文件中,再覆盖掉原文件。值为false时,字符串直接写入文件中。
另外write方法是直接覆盖掉之前的文件,此方法没有提供方式追加字符串到文件末尾。所以需要追加功能只能另外想办法了。
读取文件内容直接使用String的便利构造器来获取,该构造器在文件不存时会抛出异常,使用do...catch可以捕获,也可以使用try?获取一个可选字符串。

使用 Foundation 文件处理类读写文件

实际上,Swift 3.0 提供了 FileManagerFileHandle 进行文件相关操作。FileManager 侧重对文件夹和文件的操作,比如创建,删除,移动,复制等操作。FileManager提供了方法对文件内容进行读取,并没有写入方法:

1
2
3
4
5
let manager = FileManager.default
if let content = manager.contents(atPath: filePath) {
print(String(data: content, encoding: String.Encoding.utf8))
}

FileHandle 是对描述符操作的类封装,描述符不限于文件,也包括 socket,管道和设备描述符。大体的感觉就是标准IO操作进行了封装。对于读和写的操作,需要分开创建实例来操作。
写入操作如下代码:

1
2
3
4
5
let handler = FileHandle(forWritingAtPath: filePath)
let string = "添加一些文字到文件末尾"
let appendedData = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
handler?.seekToEndOfFile()
handler?.write(appendedData!)

使用时需要注意以下几点:

  • 字符串不能直接通过FileHandle进行写入,需要转换为Data(前身是NSData)类型进行写入。
  • 使用seekToEndOfFile追加内容到文件中,如果需要覆盖原文件,则不调用它。
  • FileHandle 读写文件的构造器都是可失败的,要么在创建实例时候做一下判断,要么直接使用可选变量,以上代码使用的是后者。

使用FileHandle读取文件内容比较简单:

1
2
3
4
5
6
7
8
9
10
let readingHandler = FileHandle(forReadingAtPath: filePath)
guard let data = readingHandler?.readDataToEndOfFile() else {
exit(0)
}
guard let content = String(data: data, encoding: String.Encoding.utf8) else {
exit(0)
}
print(content)

另外,还可以使用convenience init?(forUpdatingAtPath path: String)来创建读写操作实例,需要注意的是,写入数据到文件中会使得描述符偏移量移动到文件最后,如果这时需要读取所有内容,则需要重置描述符偏移量为0:

1
2
3
4
5
6
7
let handler = FileHandle(forUpdatingAtPath: filePath)
let string = "添加一些文字到文件末尾"
let appendedData = string.data(using: String.Encoding.utf8, allowLossyConversion: true)
handler?.seekToEndOfFile()
handler?.write(appendedData!)
handler?.seek(toFileOffset: 0) // 需要把文件描述符偏移量重置为0
let data = handler?.readDataToEndOfFile()

FileHandle还可以对 URL 进行操作,具体请参见官方文档

使用标准IO库读写文件

FileHandle使用起来不方便的地方就是需要Data类型作为输入参数,不能直接使用String作为参数传入。原因是Data还可以承载二进制内容,本文主要考虑字符串类型的写入和读取。这里有两种思路可以实现String类型的读写:

  • 扩展FileHandle
  • 使用标准IO库读写文件

这里尝试使用第二种方式,第一种读者可以自行实现。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import Foundation
#if os(Linux)
import Glibc
#endif
enum FileWriteMode {
case Write, Append
func cMode() -> String {
switch self {
case .Write: return "w+"
case .Append: return "a+"
}
}
}
class FileHelper {
var filePath: String
init(file: String) {
self.filePath = file
}
func read() -> String {
do {
let content = try String(contentsOfFile: self.filePath, encoding: String.Encoding.utf8)
return content
} catch {
return ""
}
}
func write(content: String) {
self.writeByMode(content: content, writeMode: .Write)
}
func append(content: String) {
self.writeByMode(content: content, writeMode: .Append)
}
private func writeByMode(content: String, writeMode: FileWriteMode){
content.withCString { (ptr: UnsafePointer<Int8>) -> Void in
let fd = fopen(self.filePath, writeMode.cMode())
if writeMode == .Append {
fseek(fd, 0, SEEK_END)
}
fwrite(ptr, 1, content.lengthOfBytes(using: String.Encoding.utf8), fd)
let res = fclose(fd)
if res != 0 {
print(strerror(errno))
}
}
}
}
var helper = FileHelper(file: filePath)
print(helper.read())
helper.write(content: "你好")
helper.append(content: "再见")

read方法直接使用字符串构造函数读取文件内容,而writeappend 使用标准IO库的函数进行操作。

本文未涉及到二进制文件的读写,FileHandle实际上提供了这样的操作。

文章目录
  1. 1. 使用字符串方法读写文件
  2. 2. 使用 Foundation 文件处理类读写文件
  3. 3. 使用标准IO库读写文件