en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

3.26.2012

對 Document 中的檔案存取方法

  

每個應用程式裡都有一個 Document 資料夾,內容通常存放一些動態的使用者資料,與一般暫存區不同的是 Document 資料夾中的檔案在結束應用程式之後,仍然會保留,因此你可以將一些應用程式的環境設定參數存入其中,以便在下一次執行應用程式時做環境上的設定。下面將以 plist(Property List)檔案為例,實作讀取、寫入、清除等動作。


讀取檔案
在讀取檔案的部份,首先要取得在 Document 中的檔案路徑,之後我們利用該路徑來判斷檔案是否存在,才予以讀取。
//取得plist檔案路徑
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:@"/Data.plist"];

NSFileManager *fileManager = [NSFileManager defaultManager];

//判斷plist檔案存在才讀取
if ([fileManager fileExistsAtPath: filePath]) {
    NSMutableDictionary *data = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

    [textView setText:[NSString stringWithFormat:@"%@", data]];
} else{
    [textView setText:@"沒有資料,讀取失敗!"];
}


寫入檔案
在寫入檔案的部份,主要分成設定資料與覆寫兩部分,它門主要的差異所要操作的檔案是否已經存在於 Document 中,如果檔案已經存在就直接讀取檔案資料來做修改,否則就建立新的檔案。
//取得plist檔案路徑
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:@"/Data.plist"];

NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableDictionary *data;

//判斷plist檔案是否存在於對應位置
if ([fileManager fileExistsAtPath: filePath]) {

    //讀取存在的plist檔案,之後等待覆寫
    data = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

} else {

    //在對應位置中建立plist檔案
    data = [[NSMutableDictionary alloc] init];
}

//覆寫or設定參數值
[data setValue:[NSString stringWithFormat:@"%0.f", [hp_Slider value]] forKey:@"HP"];
[data setValue:[NSString stringWithFormat:@"%0.f", [mp_Slider value]] forKey:@"MP"];
[data setValue:[NSString stringWithFormat:@"%0.f", [atk_Slider value]] forKey:@"ATK"];
[data setValue:[NSString stringWithFormat:@"%0.f", [def_Slider value]] forKey:@"DEF"];

//將plist檔案存入Document
if ([data writeToFile:filePath atomically: YES]) {
    [textView setText:@"資料寫入成功!"];
} else {
    [textView setText:@"資料寫入失敗!"];
}


清除檔案
在清除檔案的部份,同樣先判斷檔案是否存在於 Document 中,才予以刪除。
//取得plist檔案路徑
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingString:@"/Data.plist"];

NSFileManager *fileManager = [NSFileManager defaultManager];

//判斷plist檔案存在時才刪除
if ([fileManager fileExistsAtPath: filePath]) {
    [fileManager removeItemAtPath:filePath error:NULL];

    [textView setText:@"資料清除成功!"];
}


其他的資料存放路徑
在應用程式中還存在著其他的檔案存放路徑,像是常用到的專案內的靜態檔案路徑 pathForResource 或是資料暫存區 NSTemporaryDirectory() 等下面就列出一些常用的檔案存放路徑。
//專案中的靜態檔案
NSString *path = [[NSBundle mainBundle] pathForResource:@"檔名" ofType:@"副檔名"];

//應用程式的家目錄
NSString *path = NSHomeDirectory();

//應用程式的暫存區
NSString *path = NSTemporaryDirectory();

最後,如果想要更有系統的存取大筆資料,可以考慮使用內建的輕量資料庫 SQLite3,關於 SQLite3 的基本使用方式可以參考 SQLite3 的取值方式一文。





19 則留言:

  1. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];


    請問這一段是什麼意思呢?
    新手上路

    回覆刪除
    回覆
    1. 便當您好:

      NSArray *paths 這一行主要是告訴系統要去哪裡找檔案,癟就是我們告訴系統你只能在這個範圍「NSUserDomainMask」裡尋找檔案。

      NSString *documentsDirectory 則是把路徑 paths 轉換成字串,因為上述程式碼所得到的 path 是一個陣列,你可以自己將他傾印出來看看,
      [paths objectAtIndex:0] ,只是指到這個陣列的第一個位置,就這樣!

      這兩段程式碼和在一起就是,去目標位址找出檔案的存放路徑。

      刪除
  2. 匿名7/02/2013

    //在對應位置中建立plist檔案
    data = [[NSMutableDictionary alloc] init];
    我試著copy這段code至程式內,發現位置內也並沒有plist檔案產生,
    請問我該如何由code中建立檔案

    回覆刪除
    回覆
    1. 您好:

      請問您是如何檢視沒有產生,它是屬於隱藏的資料夾,是無法從專案中看到...

      你可以試著取值回來就會知道有沒有成功

      刪除
  3. 匿名7/03/2013

    if ([fileManager fileExistsAtPath: filePath]) {

    //讀取存在的plist檔案,之後等待覆寫
    data = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];

    } else {

    //在對應位置中建立plist檔案
    data = [[NSMutableDictionary alloc] init];
    }
    您好!我複製這段程式碼這在其中加上NSlog顯示是否成功.結果一直是失敗的訊息,而且我用finder找尋filePath(我有用NSlog將路徑印出來)先找Library-->application support-->iphone-->6.0....結果其中真的並無檔案產生.....我先問如果要生成檔案的指令是否就是
    data = [[NSMutableDictionary alloc] init];

    回覆刪除
    回覆
    1. 您好

      因為你沒有檔案才會失敗,這邊並沒有建立檔案,是假設檔案已經存在了!

      data = [[NSMutableDictionary alloc] init]; 只是對NSMutableDictionary初始化

      刪除
  4. 匿名7/04/2013

    您好!
    那請教一下.可以從程式中create檔案嗎?
    如果可以那我應該如何去做?

    謝謝您的細心解答!

    回覆刪除
  5. 您好
    我在stackoverflow中幫您找了這一篇,除了動態產生plist檔案之外,他還可以賦予key值,並存放於Documents中,可以參考看看。

    http://stackoverflow.com/questions/6697247/how-to-create-plist-files-programmatically-in-iphone

    回覆刪除
  6. 匿名7/04/2013

    您好!
    謝謝您抽空幫我找資訊...
    不過看過之後
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSMutableDictionary *data;

    if ([fileManager fileExistsAtPath: path])
    {
    data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
    }
    else
    {
    // If the file doesn’t exist, create an empty dictionary
    data = [[NSMutableDictionary alloc] init];
    }
    這一段跟您所寫的不是一樣嗎?
    還是小弟我找錯了嗎?
    再麻煩您了!謝謝您

    回覆刪除
  7. 匿名7/08/2013

    您好
    看過您給的網站發現
    if ([fileManager fileExistsAtPath: path])
    {
    data = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
    }
    else
    {
    // If the file doesn’t exist, create an empty dictionary
    data = [[NSMutableDictionary alloc] init];
    }
    這一段不是跟您所寫的一樣嗎?
    順便請教您.您寫這篇文章時,Plist檔案您是如何產生的....?
    謝謝您

    回覆刪除
    回覆
    1. 您好:

      我是使用動態方式產生的plist的,在建立檔案的部分 [data writeToFile:filePath atomically: YES] 才是將檔案寫入目標位置的程式碼,您所提到的
      data = [[NSMutableDictionary alloc] init] 這邊只是將NSData初始化,並沒有任何寫入的動作。

      如果你有任何問題,歡迎你來信至 furnacedigital@gmail.com ,我們很樂意與您分享這個範例的程式碼。

      刪除
  8. 這篇是找到Document裡的plist檔案然後修改,請問:如果是別的檔,如 js (javascript),要如何讀取其內容並修改? 謝謝

    回覆刪除
    回覆
    1. 您好:

      使用 NSFileManager 可以針對檔案做移動、複製、刪除、讀取和改寫入動作,但是這些皆不包含改變檔案的內容,如果想要製作文字檔案像是 txt html 或是您提到的 js 等等,可以使用 NSMutableString 來整合這些字串集,或是使用 NSString 的初始畫函式 initWithContentsOfFile: encoding: error: 來取得檔案內的字串。 - See more at: http://furnacedigital.blogspot.tw/2012/03/document.html?showComment=1376458901133#c88967856429912081

      刪除
  9. 匿名7/11/2014

    請問 plist是不是不能存入中文呢?小弟想用plist當成所有class的全域變數,但發現無法存入中文,需要轉碼,請問有可以讓變數在全部class通用的方法嗎?感謝

    回覆刪除
    回覆
    1. 您好:
      不能用中文、就算可以也盡量避免,才不會有編碼上的錯誤。
      另外讓變數在全部class通用的方法,你可以參考這一篇:
      http://furnacedigital.blogspot.tw/2012/07/class.html

      sigleton class 可以 解決你的需求

      刪除
  10. 豆腐1/29/2015

    您好,看到上一個問題,想請問使用plist當全域變數不好嗎?如果需求是在程式重啟後變數仍存在,那是不是使用plist是最好的方法?謝謝您,您的blog真的對初學者幫助非常的大!

    回覆刪除
    回覆
    1. 豆腐 您好:

      plist當全域變數這個問題,當然ok的,不過有可能導致以下這些問題產生。

      - 必須在每個class中都包含相同的程式碼「讀取」與「存入」,這些指令不像我們在使用一般變數一樣簡單。
      - 另一個就是我們在玩game時會遇到的存檔機制,存檔,遇到過不了的地方(bug),關掉遊戲再讀取檔案,如果不把plist和全域變數拆開設計,這部份會很難實行。

      最後,如果你的程式很小可以忽略這些,當然plist是可以當做全域變數使用,但是plist還有一個無法取代全域變數的地方,就是當app關閉時,全域變數便會「釋放」,plist卻會存在你的檔案資料夾中....直到永遠!

      刪除
    2. 豆腐 您好:

      不知道為什麼我看不到你的留言,只有看到 blogger 轉發的 mail。
      如果你的目的是「但我希望變數在程式重開啟後仍存在」,這個就是存檔機制啦,既然是存「檔」,表示要有地方放這些資料,這時候你說的plist就是一個可行的例子,不過在執行階段(run time)通常還是會用全域變數以較方便,只要在結束時或是特定時間轉存回plist上就可以了。

      所以我才會以game存檔的例子來舉例,你的思路也沒問題!!

      刪除
  11. 豆腐1/30/2015

    但我希望變數在程式重開啟後仍存在,使用全域變數會釋放,這樣不就無法實現存檔機制了?以您提到game存檔的例子,所以我應該是要在程式運行時使用全域變數,而當要進行存檔動作時再將其寫入plist、sqlite、xml等資料中,這樣的思路對嗎?新手上路很感謝您的細心指教。

    回覆刪除