(挖坑自己跳系列二)
上回说到文件操作,那就把文件操作来一遍吧,先放源码:GitHub某个地方
初始化 & 文件路径
所有的文件操作都要先声明一个根控制器以获得文件的句柄:
let manager = NSFileManager.defaultManager()
而文件路径的表示有两种方式:String和NSURL。在NSURL的视角中,本地文件作为一种特殊的网络文件而存在,以“file://”开头。多数的操作对于两种路径方式都有两个相对应的接口,要注意对于NSURL,表示本地文件时必须以“file://”开头,否则很多API会失败。为了测试方便,我给String加了个转换的扩展:
extension String {
var toFilePathURL : NSURL {
let url = NSURL( fileURLWithPath: self)
return url
}
}
这里使用参数fileURLWithPath:而不是string:就是为了上述原因。
对于路径的修改,String表示方法很简单,直接相加即可;NSURL需要用以下代码:
你看到的是非授权版本!爬虫凶猛,请尊重知识产权!
转载请注明出处:http://conanwhf.github.io/2015/12/15/fileop/
访问原文「文件操作」获取最佳阅读体验并参与讨论
let newUrl = url.URLByAppendingPathComponent("/urlNewDir/", isDirectory: true)
//当然false也是可以的,但要注意匹配,别把参数给错了
获取用户目录
由于Apple的沙盒机制,平时在iOS上能操作的文件也只存在于App当前用户目录、临时文件之类的地方而已,获得这个地址的方法是:
NSHomeDirectory()
NSHomeDirectoryForUser("Conan")
另外还有一些别的”NSXXXDirectory()”,大家自己可以去探索下。对于NSURL的方法如下:
let urlForDocument = manager.URLsForDirectory( NSSearchPathDirectory.DocumentDirectory, inDomains:NSSearchPathDomainMask.UserDomainMask) //获得用户Document目录
let url = urlForDocument[0] as NSURL
返回的是Document目录。第一句其实是获得一个搜索集,目标由NSSearchPathDirectory.XXX参数指定,在我们知道只有一个搜索结果的情况下,可以直接指定数组中的[0]
在第一种方法中,我还没找到怎么拿到Document文件夹,谁知道麻烦留言告诉我,谢谢!🤗
在Playground中跑的时候,用户环境其实是在模拟器中,各种权限问题和奇怪的找不到文件之类,调试也不太方便,我就在此吃了点亏。建议有想不通错哪里的时候去终端跑一下,说不准就OK了(怎么跑见前一篇)。
从这里开始进入罗列模式,因为文件操作的很多API实在雷同。我默认文件路径方面已经没有问题了,且你也申明了文件的根控制器manager,那么基本上就是使用:manager.TODOWHAT(URL/PATH),如果有针对String和URL的不同API我会分别列出,写两次。
判断文件或者文件夹是否存在
首先是判断文件是否存在:
manager.fileExistsAtPath(workdir) //文件夹
manager.fileExistsAtPath(workdir + "aaa.txt”) //文件
新建文件
创建文件夹
do {
try manager.createDirectoryAtPath(fn, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Error by createDirectoryAtPath: \(error)\n")
}
do {
try manager.createDirectoryAtURL(fnUrl, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Error by createDirectoryAtURL: \(error)\n")
}
其中参数_withIntermediateDirectories_为true则表示路径中间如果有不存在的文件夹都会一并创建
创建普通文件
fn = workdir + "try.txt"
manager.createFileAtPath(fn, contents: nil , attributes: nil)
创建符号链接
do {
try manager.createSymbolicLinkAtPath(workdir + "tryLink.txt", withDestinationPath: fn)
} catch {
print("Error by createSymbolicLinkAtPath: \(error)\n")
}
do {
let linkurl = url.URLByAppendingPathComponent("link-url", isDirectory: false)
try manager.createSymbolicLinkAtURL(linkurl, withDestinationURL: fnUrl)
} catch {
print("Error by createSymbolicLinkAtURL: \(error)\n")
}
以上的文件创建,在默认的情况下如果已有目标文件存在则会失败;除了createFileAtPath会直接覆盖掉。
读取文件
读取文件的数据结果为NSData,如果需要变成可读的文字,转换成String即可:
let st = String(data: data!, encoding: NSUTF8StringEncoding)
而读取文件有两种方式,一种直接获取全部数据:
data = manager.contentsAtPath(fn)
另一种是使用文件句柄:
let handler = NSFileHandle(forReadingAtPath: fn)
data = handler?.readDataToEndOfFile()
do {
let handler = try NSFileHandle(forReadingFromURL: fnUrl)
data = handler.readDataToEndOfFile()
} catch {
print("Error by NSFileHandle: \(error)\n")
}
第二种方式比较灵活,还有别的参数可用,通常结合写数据用来修改文件
将数据写入文件
整体数据写入
可以通过writeToFile方法,创建并将数据整体写入文件。支持的数据对象包括String,NSString,UIImage,NSArray,NSDictionary等。
字符串
let info = "测试数据1234"
do {
try info.writeToFile(fn + "_string.txt", atomically: true, encoding: NSUTF8StringEncoding)
} catch {
print("Error by writeToFile: \(error)\n")
}
图片
let image = UIImage(named: workdir + "/test/207006981.jpg”)//这里只是先load一下
let data:NSData = UIImagePNGRepresentation(image!)!
data.writeToFile(fn + "_img.jpg", atomically: true)
数组
let array = NSArray(objects: "aaa","bbb","ccc")
array.writeToFile(fn + "_arr.txt", atomically: true)
字典
let dictionary = NSDictionary(objects: ["111","222"], forKeys: ["aaa","bbb"]
dictionary.writeToFile(fn + "_dic.txt", atomically: true)
末尾添加、修改文件
修改文件跟C的用法很像,使用文件句柄,结合seek到的某个位置,写入数据。需要注意的是没有专门的插入数据方法,所以如果你想插入数据,就需要把后面的数据都出来(或者另存下来),添加数据,再续上之前的后半截。所以如果数据量大,下面的暴力方法不合适,需要用callback结合缓存去做读写。
末尾添加数据
let handler = NSFileHandle(forUpdatingAtPath: fn)
guard handler != nil else {
print("No such file")
return
}
string = "\n用forUpdatingAtPath在文件末尾添加XXX"
data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
handler!.seekToEndOfFile()
handler!.writeData(data)
插入数据
var string = "\n用forUpdatingAtURL在文件第10个字节插入XXX\n"
var data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)!
do {
let handler = try NSFileHandle(forUpdatingURL: fnUrl)
handler.seekToFileOffset(10)
let data2 = handler.readDataToEndOfFile()
handler.seekToFileOffset(10)
handler.writeData(data)
handler.writeData(data2)
} catch {
print("Error by NSFileHandle: \(error)\n")
}
这里两种方式用了不同的句柄获得方式,实际上可以随意互换。
复制文件(夹)
do {
try manager.copyItemAtPath(src, toPath: dest)
} catch let error as NSError {
print("Error by Path: \(error)\n")
}
do {
try manager.copyItemAtURL( srcUrl, toURL: destUrl)
} catch let error as NSError {
print("Error by URL: \(error)\n")
}
移动文件(夹)
do {
try manager.moveItemAtPath(src, toPath: dest)
} catch {
print("Error by Path: \(error)\n")
}
do {
try manager.moveItemAtURL( srcUrl, toURL: destUrl)
} catch {
print("Error by URL: \(error)\n")
}
删除文件(夹)
do {
try manager.removeItemAtPath(fn)
} catch {
print("Error by Path: \(error)\n")
}
do {
try manager.removeItemAtURL( fn.toFilePathURL)
} catch {
print("Error by URL: \(error)\n")
}
遍历文件夹
遍历文件夹有两种方式:遍历当前文件夹,和递归遍历子文件夹。下面是范例。注意:使用Path为参数的时候返回的是相对路径和文件名,而使用URL方式时返回的是绝对路径!
不遍历子文件夹
do {
let contentsOfPath = try manager.contentsOfDirectoryAtPath(dir)
contentsOfPath.forEach{ print($0) }
} catch {
print("Error by 1-1: \(error)\n")
}
do {
let contentsOfURL = try manager.contentsOfDirectoryAtURL(url, includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles)
contentsOfURL.forEach{ print($0) }
} catch {
print("Error by 1-2: \(error)\n")
}
递归遍历子文件夹,但不递归符号链接
let enumeratorAtPath = manager.enumeratorAtPath(dir)
if enumeratorAtPath == nil {
print("Error by 2-1: no such folder: \(dir)")
//return
}
else {
enumeratorAtPath!.forEach{ print($0) }
}
let enumeratorAtURL = manager.enumeratorAtURL(url,includingPropertiesForKeys: nil, options: NSDirectoryEnumerationOptions.SkipsHiddenFiles, errorHandler:nil)
if enumeratorAtURL == nil {
print("Error by 2-2: no such folder: \(url)")
//return
}
else {
enumeratorAtURL!.forEach{ print($0) }
}
递归遍历子文件夹,包括符号链接
let subpathsAtPath = manager.subpathsAtPath(dir)
if subpathsAtPath == nil {
print("-Error by 3: no such folder: \(dir)")
//return
}
else {
subpathsAtPath!.forEach{ print($0) }
}
这个函数有点危险,可能一不小心就死循环了,建议慎用。
获取文件属性和权限
权限判断
let readable = manager.isReadableFileAtPath(fn)
let writeable = manager.isWritableFileAtPath(fn)
let executable = manager.isExecutableFileAtPath(fn)
let deleteable = manager.isDeletableFileAtPath(fn)
print("文件\(fn) \(readable ? "" : "不")可读, \(writeable ? "" : "不")可写,\(executable ? "" : "不")可执行,\(deleteable ? "" : "不")可删除")
获取文件属性
do {
let attributes = try manager.attributesOfItemAtPath(fn)
print("attributes: \(attributes)")
} catch {
print("Error by attributesOfItemAtPath: \(error)\n")
}
比较文件(夹)
manager.contentsEqualAtPath(workdir + "read.md", andPath: workdir + "copied.txt") //文件
manager.contentsEqualAtPath(workdir, andPath: workdir + "/tesst/") //文件夹
这个函数不是diff,只能判断是否相同,不能做内容对比