---
title: 'Objective-C 檔案、目錄'
disqus: kyleAlien
---
Objective-C 檔案、目錄
===
## Overview of Content
[TOC]
## NSFileManager 檔案管理
`NSFileManager` 類可以用來管理、查詢、操作檔案(並不是指檔案內容,而是檔案本身)
```objectivec=
#import <Foundation/NSFileManager.h>
```
### 檔案路徑 - NSString
* **`NSFileManager#currentDirectoryPath` 方法**:取得當前路徑
```objectivec=
void path_1(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 取得當前路徑的最後
NSString *curPath = [fm currentDirectoryPath];
NSLog(@"Current path: %@", curPath);
}
```
> 
* **`lastPathComponent` 方法**:取得當前路徑的後一個元素
```objectivec=
void path_2(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *curPath = [fm currentDirectoryPath];
// 取得當前路徑的最後元素
NSString *lastComponent = [curPath lastPathComponent];
NSLog(@"lastComponent: %@", lastComponent);
}
```
> 
* **`stringByAppendingPathComponent`、`pathExtension` 方法**:組合路徑、取得當前組合路徑的副檔名
```objectivec=
void path_3(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *curPath = [fm currentDirectoryPath];
NSLog(@"Current path: %@", curPath);
NSString *fullPath = [curPath stringByAppendingPathComponent:@"Hello.123"];
NSLog(@"Full path: %@", fullPath);
NSString *extDir = [fullPath pathExtension];
NSLog(@"Ext dir name: %@", extDir);
}
```
> 
### 檔案 - 是否存在、屬性
* **`fileExistsAtPath` 方法**:可以判斷檔案是否存在
```objectivec=
void isFileExist(void) {
NSFileManager *fm = [NSFileManager defaultManager];
BOOL isExist = [fm fileExistsAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"];
NSLog(@"isFileExist: %i\n", isExist);
}
```
> 
* **`fileExistsAtPath` 方法**:可以取得檔案是屬性(檔案所屬、ID、權限、類型... 等等)
```objectivec=
void fileAttr(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSDictionary *dic = [fm fileAttributesAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"
traverseLink:YES];
NSLog(@"file attr: %@", dic);
}
```
> 
:::warning
* 上面範例的方法已經遺棄,可以改用 `attributesOfItemAtPath` 方法
```objectivec=
void fileAttr2(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 創建指標,並非物件
NSError *error;
NSDictionary *attr = [fm attributesOfItemAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"
error:&error];
if(attr == nil) {
NSLog(@"Error file attr: %@", error);
} else {
NSLog(@"File attr: %@", attr);
}
}
```
> 
:::
### 檔案 - 複製、比對、刪除、移動
* **`copyPath` 方法**:用來複製檔案
:::warning
該方法已經被遺棄,建議使用 `NSData` or `NSFileHandle` 類操作
:::
```objectivec=
void cpFile(void) {
NSString *targetFile = @"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt";
NSString *cpFile = @"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello_2.txt";
NSFileManager *fm = [NSFileManager defaultManager];
BOOL isSuccess = [fm copyPath:targetFile
toPath: cpFile
handler:nil];
if(!isSuccess) {
NSLog(@"copy file failure.");
return;
}
NSLog(@"copy file success.");
}
```
> 
* **`contentsEqualAtPath` 方法**:比對檔案的大小、內容,如果都相同則返回 True
```objectivec=
void compareFile(void) {
// 比較大小、內容
NSFileManager *fm = [NSFileManager defaultManager];
BOOL isSame = [fm contentsEqualAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"
andPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello_2.txt"];
if(!isSame) {
NSLog(@"The files not same.");
return;
}
NSLog(@"The files same.");
}
```
> 
* **`removeItemAtPath` 方法**:指定移除位置的檔案
```objectivec=
void deleteFile(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 創建指標,並非物件
NSError *error;
BOOL isRemoved = [fm removeItemAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello_2.txt"
error:&error];
if(isRemoved) {
NSLog(@"File deleted successfully.");
} else {
NSLog(@"Error deleting file: %@", error);
}
}
```
> 
* **`moveItemAtPath` 方法**:移動指定檔案到指定目錄
:::success
在移動的同時還 **可以順便換檔案名**
:::
```objectivec=
void moveFile(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 創建指標,並非物件
NSError *error;
// 移動還可以順便換檔案名
BOOL isMoved = [fm moveItemAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Test22.txt"
toPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/123/Yes123.txt"
error:&error];
if(isMoved) {
NSLog(@"File move successfully.");
} else {
NSLog(@"Error move file: %@", error);
}
}
```
> 
### 目錄 - 取得當前目錄、切換目錄
* **`currentDirectoryPath` 方法**:取得當前目錄
```objectivec=
void returnCurrentDirectory(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString * cPath = [fm currentDirectoryPath];
NSLog(@"Current diectory: %@", cPath);
}
```
> 
* **`changeCurrentDirectoryPath` 方法**:移動到指定目錄
```objectivec=
void cdDirectory(void) {
NSFileManager *fm = [NSFileManager defaultManager];
BOOL isSuccess = [fm changeCurrentDirectoryPath:@"/Users/Hy-KylePan/Documents/iOS"];
if(!isSuccess) {
NSLog(@"Change diectory failure.");
return;
}
NSString * cPath = [fm currentDirectoryPath];
NSLog(@"Current diectory: %@", cPath);
}
```
> 
### 目錄 - 創建、刪除目錄
* **`createDirectoryAtPath` 方法**:在指定路徑之下創建目錄
```objectivec=
void createDirectory(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 創建指標,並非物件
NSError *error;
BOOL isSuccess = [fm createDirectoryAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Yoyo"
withIntermediateDirectories:YES
attributes:nil
error:&error];
if(!isSuccess) {
NSLog(@"Create diectory failure. %@", error);
return;
}
NSLog(@"Create diectory success.");
}
```
> 
* **`removeItemAtPath` 方法**:移除指定路徑之下的目錄
> 這個方法與移除檔案相同,也就是該方法可以移除檔案、目錄
```objectivec=
NSFileManager *fm = [NSFileManager defaultManager];
// 創建指標,並非物件
NSError *error;
BOOL isSuccess = [fm removeItemAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Yoyo"
error:&error];
if(!isSuccess) {
NSLog(@"Delect diectory failure. %@", error);
return;
}
NSLog(@"Delect diectory success.");
}
```
### 目錄 - 遍歷目錄
* **`enumeratorAtPath` 方法**:該方法可以取得指定目錄下的 **所有檔案**(包括子檔案、目錄),並以 `NSDirectoryEnumerator` 的方式返回
```objectivec=
void recursiveList(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 移動到指定目錄
[fm changeCurrentDirectoryPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest"];
NSDirectoryEnumerator *enumItems = [fm enumeratorAtPath:[fm currentDirectoryPath]];
// 會往內部一直遞迴搜尋
NSString *path;
while ((path = [enumItems nextObject]) != nil) {
NSLog(@"path: %@", path);
}
}
```
:::info
可以看到這裡使用遞迴的方式遍歷指定目錄下的所有檔案(用 Tree 方式訪問)
:::
> 
* 如果只想訪問指定目錄下的檔案資料夾(不包括所有子檔案、資料夾)
```objectivec=
void showDirectoryList(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *curPath = [fm currentDirectoryPath];
NSLog(@"Current path: %@", curPath);
NSArray *array = [fm directoryContentsAtPath:curPath];
// 會往內部一直遞迴搜尋
NSString *path;
for(path in array) {
NSLog(@"path: %@", path);
}
}
```
> 
:::warning
* 由於以上方法已遺棄,可以改用 `` 方法
```objectivec=
void showDirectoryList2(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSString *curPath = [fm currentDirectoryPath];
NSLog(@"Current path: %@", curPath);
// 創建指標,並非物件
NSError *error;
NSArray *array = [fm contentsOfDirectoryAtPath:curPath
error:&error];
if(error != nil) {
NSLog(@"Get directory failure: %@", error);
return;
}
// 會往內部一直遞迴搜尋
NSString *path;
for(path in array) {
NSLog(@"path: %@", path);
}
}
```
> 
:::
## NSData 檔案內容
每個檔案中放置的內容 (或是要放置進檔案的內容) 在 Objective-C 中是使用 `NSData` 來表示
```objectivec=
#import <Foundation/NSData.h>
```
### 讀取檔案資料
* 範例:
1. 透過 `NSData#contentsAtPath` 讀取檔案的資料為 `NSData`
2. 再透過 `NSString#initWithData` 讀取 NSData 的檔案並顯示
```objectivec=
void readFile(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSData *data = [fm contentsAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"];
if(data == nil) {
NSLog(@"Error read file");
} else {
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"Read file: %@", content);
}
}
```
> 
### 將資料寫入檔案
* 範例:
1. 透過 `NSString#stringWithContentsOfFile` 建立資料(`NSData`)
2. 再透過 `NSFileManager#createFileAtPath` 創建檔案並寫入 NSData 數據到檔案中
```objectivec=
void writeFile(void) {
NSFileManager *fm = [NSFileManager defaultManager];
// 透過 NSString,創建 NSData
NSString *myString = @"Hello, World!";
NSData *data = [myString dataUsingEncoding:NSUTF8StringEncoding];
if(data == nil) {
NSLog(@"Error open file");
} else {
BOOL isSuccess = [fm createFileAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/World.txt"
contents:data
attributes:nil];
if(!isSuccess) {
NSLog(@"Write file error");
return;
}
NSString *content = [NSString
stringWithContentsOfFile:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/World.txt"
encoding:NSUTF8StringEncoding
error:nil];
NSLog(@"Read, write file: %@", content);
}
}
```
> 
## NSFileHandle 檔案內容控制
`NSData` 可以讀取、寫入資料,但並不能控制檔案內容;如果要控制檔案內容則必須使用 `NSFileHandle` (可以做到更細緻的操作)
```objectivec=
#import <Foundation/NSFileHandle.h>
```
### 操作讀取檔案 - fileHandleForReadingAtPath
* 使用 `NSFileHandle#fileHandleForReadingAtPath` 可以用來控制指定檔案的內容
```objectivec=
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:<路徑>];
```
* **`readDataToEndOfFile` 方法**:使用該方法可以讀取全部檔案內容
```objectivec=
void handleReadFile(void) {
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"];
if(handle == nil) {
NSLog(@"handle read file failure.");
} else {
// 取讀取全部內容
NSData *data = [handle readDataToEndOfFile];
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"handle read file success: %@", content);
}
}
```
> 
* **`readDataOfLength` 方法**:使用該方法可以讀取指定檔案的指定 Byte 數
```objectivec=
void handleReadFile2(void) {
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:@"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/Hello.txt"];
if(handle == nil) {
NSLog(@"handle read file failure.");
} else {
// 取讀取指定 byte 數量
NSData *data = [handle readDataOfLength:3];
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"handle read file success: %@", content);
}
}
```
> 
### 操作寫入檔案 - fileHandleForWritingAtPath
* 使用 `NSFileHandle#fileHandleForWritingAtPath` 可以用來控制指定檔案的內容
```objectivec=
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:<路徑>];
```
**`writeData` 方法**:透過該方法可以對檔案寫入資料
```objectivec=
void handleWriteFile(void) {
NSString *filePath = @"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/World.txt";
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
if (handle == nil) {
NSLog(@"Handle read file failure.");
} else {
// 創建帶有文字內容的 NSData 物件
NSString *content = @"Hello, this is the content to write to the file.";
NSData *data = [content dataUsingEncoding:NSUTF8StringEncoding];
[handle writeData:data];
// 用於確認寫入的內容
NSString *readContent = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
NSLog(@"Update file: %@", readContent);
}
}
```
> 
<!-- ### 操作更新檔案 -->
### 檔案內容偏移量控制
* **`offsetInFile` 方法**:該方法可以讀取當前檔案的偏移量(也就是你讀取到哪裡)
```objectivec=
void fileOffect(void) {
NSString *filePath = @"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/World.txt";
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:filePath];
// 開始讀取前的偏移量
unsigned long long offect = [handle offsetInFile];
NSLog(@"Before read, file offect: %lli", offect);
// 讀到結尾
[handle readDataToEndOfFile];
// 開始讀取後的偏移量
offect = [handle offsetInFile];
NSLog(@"After read, file offect: %lli", offect);
}
```
> 
* **`seekToFileOffset` 方法**:該方法可以直接控制偏移量
```objectivec=
void fileOffestSeek(void) {
NSString *filePath = @"/Users/Hy-KylePan/Documents/iOS/fileTest/fileTest/World.txt";
NSFileHandle *handle = [NSFileHandle fileHandleForReadingAtPath:filePath];
// 控制偏移量,位移 20 byte
[handle seekToFileOffset:20];
// 從 20 byte 之後開始讀取,讀至結尾
NSData *data = [handle readDataToEndOfFile];
NSString *content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"After seek 20, content: %@", content);
[handle closeFile];
}
```
> 
## 其他
### 暫存資料夾
* 使用 `NSFileManager#temporaryDirectory` 方法,就可以取得暫存的資料夾空間
```objectivec=
void showTempDirectory(void) {
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *url = [fm temporaryDirectory];
NSLog(@"temp directory: %@", url);
}
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `iOS`