第一版是針對 Lua 5.0 編寫的。雖然在很大程度上仍然適用於後續版本,但有些地方有所不同。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買這本書,您也可以協助 支援 Lua 專案


第 12 章 – 資料檔案和持久性

在處理資料檔案時,通常寫入資料比讀回資料容易得多。當我們寫入檔案時,我們完全控制正在進行的作業。另一方面,當我們讀取檔案時,我們不知道會讀到什麼。除了正確檔案可能包含的各種資料之外,健全的程式還應該優雅地處理錯誤的檔案。因此,編寫健全的輸入常式總是困難的。

正如我們在 第 10.1 節 的範例中所見,表格建構函式為檔案格式提供了有趣的替代方案。在寫入資料時多花一點功夫,讀取就會變得非常容易。這個技巧是將我們的資料檔案寫成 Lua 程式碼,當執行時,會將資料建置到程式中。使用表格建構函式,這些區塊看起來非常像純文字資料檔案。

照常,讓我們看一個範例來說明。如果我們的資料檔案採用預先定義的格式,例如 CSV(逗號分隔值),我們幾乎沒有選擇。(在 第 20 章 中,我們將看到如何在 Lua 中讀取 CSV。)但是,如果我們要建立檔案以供以後使用,我們可以使用 Lua 建構函式作為我們的格式,而不是 CSV。在此格式中,我們將每筆資料記錄表示為 Lua 建構函式。我們不寫入類似

    Donald E. Knuth,Literate Programming,CSLI,1992
    Jon Bentley,More Programming Pearls,Addison-Wesley,1990
這樣的內容到我們的資料檔案中,而是寫入
    Entry{"Donald E. Knuth",
          "Literate Programming",
          "CSLI",
          1992}
    
    Entry{"Jon Bentley",
          "More Programming Pearls",
          "Addison-Wesley",
          1990}
請記住,Entry{...}Entry({...}) 相同,也就是呼叫函式 Entry,其單一引數為表格。因此,這段先前的資料是 Lua 程式。若要讀取這個檔案,我們只需要執行它,並針對 Entry 提供一個明智的定義。例如,下列程式會計算資料檔案中的項目數目
    local count = 0
    function Entry (b) count = count + 1 end
    dofile("data")
    print("number of entries: " .. count)
下一個程式會在一個集合中收集檔案中找到的所有作者名稱,然後印出它們。(作者名稱是每個項目中的第一個欄位;因此,如果 b 是項目值,b[1] 就是作者。)
    local authors = {}      -- a set to collect authors
    function Entry (b) authors[b[1]] = true end
    dofile("data")
    for name in pairs(authors) do print(name) end
請注意這些程式片段中的事件驅動方法:Entry 函式作為回呼函式,會在 dofile 期間針對資料檔案中的每個項目呼叫。

如果檔案大小不是一大考量,我們可以使用名稱值對來表示

    Entry{
      author = "Donald E. Knuth",
      title = "Literate Programming",
      publisher = "CSLI",
      year = 1992
    }
    
    Entry{
      author = "Jon Bentley",
      title = "More Programming Pearls",
      publisher = "Addison-Wesley",
      year = 1990
    }
(如果這個格式讓您想起 BibTeX,這並非巧合。BibTeX 是 Lua 中建構函式語法的靈感來源之一。)這種格式就是我們所謂的自描述資料格式,因為每個資料都附有其意義的簡短描述。自描述資料比 CSV 或其他精簡符號更容易閱讀(至少對人類而言);必要時,它們很容易手動編輯;而且它們允許我們進行小修改,而無需變更資料檔案。例如,如果我們新增一個欄位,我們只需要在讀取程式中進行小幅變更,以便在欄位不存在時提供預設值。

使用名稱值格式後,我們收集作者的程式會變成

    local authors = {}      -- a set to collect authors
    function Entry (b) authors[b.author] = true end
    dofile("data")
    for name in pairs(authors) do print(name) end
現在欄位的順序無關緊要。即使有些項目沒有作者,我們只需要變更 Entry
    function Entry (b)
      if b.author then authors[b.author] = true end
    end

Lua 不僅執行速度快,編譯速度也快。例如,上面列出作者的程式在 2 MB 的資料中執行時間不到一秒鐘。同樣地,這並非偶然。資料描述一直是 Lua 自創建以來的主要應用程式之一,我們非常注意讓其編譯器對大型區塊執行得很快。