擴充識別字元

lua-users home
wiki

在討論過「如果在 Lua 識別字元中「?」和「!」是合法字元那該有多好」之後,我發現只要更改地區設定就可以做到這一點,因為 Lua 會辨識地區設定。只要建立一個其 LC_CTYPE 可以識別「?」和「!」為字母的地區設定即可。

通常來說,散佈非 C 地區設定的 Lua 程式時會遇到一個問題,因為這會讓檔案依賴於地區設定。不過,利用一些小技巧,就可以用與地區設定有關的函式取代各種讀取檔案的 Lua 函式。

以下範例只會讓 require 與地區設定有關,但那是最困難的情況;取代 `loadfile` 要容易得多。它假設檔案以標準 Unix「shebang」行開頭,外觀類似於

#!/bin/env LC_CTYPE=pt_BR lua

下方定義的 loader 會在 shebang 行中檢查看似 LC_CTYPE 定義的任何內容,並在剖析檔案時適當地設定地區設定。此功能尚未經過太多測試,但它似乎足夠有用而值得記錄下來。

local pkg = require"package"
local function findfile(pname, pathtype)
  local path = pkg[pathtype]
  local fname = pname:gsub("%.", "/")
  local errs = {} 
  for seg in path:gmatch"[^;]+" do
    local fname = seg:gsub("%?", fname)
    local file, err = io.open(fname)
    if file then
      return file, fname
    else
      errs[#errs+1] = ("\n\tno file '%s'"):format(fname)
    end
  end
  return nil, table.concat(errs)
end
  
local yield = coroutine.yield
local function Reader(file)
  return coroutine.wrap(function()
    local data = file:read(1)
    if data ~= '#' then
      yield(data)
      yield(file:read"*a")
    else
      file:seek("set")
      local lines = file:lines() 
      local shebang = lines()
      local ctype = shebang:match"LC_CTYPE=(%S+)"
      if ctype then os.setlocale(ctype, "ctype") end
      yield"\n"
      for l in lines do yield(l.."\n") end
    end 
  end)
end
  
local function myloader(pname)
  local file, fname = findfile(pname, "path")
  if file then
    local locale = os.setlocale(nil, "ctype")
    local chunk, err = load(Reader(file), "@"..fname)
    os.setlocale(locale, "ctype")
    file:close()
    if chunk then
      return chunk
    else
      error(("error loading '%s' from '%s':\n\t%s"):format(pname, fname, err), 0)
    end
  else return fname end
end
    
pkg.loaders[2] = myloader

最近變更 · 喜好設定
編輯 · 歷史記錄
上次編輯於 2007 年 7 月 13 日 格林威治標準時間上午 6:10 (diff)