Lua:如何使os.rename和os.remove使用包含Unicode字符的文件名?

时间:2019-01-09 21:18:00

标签: lua unicode-string

如何仅使用备用Lua 5.3使os.remove和os.rename与包含Unicode字符的文件名一起使用?

filename = "C:\\τέστ.txt"
os.rename(filename, filename .. "1")

这什么也没做。

我也尝试过此操作,但仍然无法正常工作

filename = "C:\\τέστ.txt"
t = {}
for p, c in utf8.codes(filename) do 
  t[#t+1] = c
end
filename = "\\" .. table.concat(t, "\\")
os.rename(filename, filename .. "1")

有什么想法吗?预先非常感谢您的帮助! :)

5 个答案:

答案 0 :(得分:3)

lhf指出,您的代码在MacOS上可以正常运行。
您所需要的只是Windows的更正。

以下代码是用纯Lua编写的;它重新定义了标准os / io函数,以便它们在Windows中以UTF-8文件名运行。
请注意,您的Windows语言环境必须为希腊语,并且所有文件名都必须仅包含Windows Greek codepage中的符号。在纯Lua的Windows上,您无法打开名称中包含任意UTF-8符号的文件。

if (os.getenv"os" or ""):match"^Windows" then

   local map_unicode_to_1253 = {
      [0x20AC] = 0x80,
      [0x201A] = 0x82,
      [0x0192] = 0x83,
      [0x201E] = 0x84,
      [0x2026] = 0x85,
      [0x2020] = 0x86,
      [0x2021] = 0x87,
      [0x2030] = 0x89,
      [0x2039] = 0x8B,
      [0x2018] = 0x91,
      [0x2019] = 0x92,
      [0x201C] = 0x93,
      [0x201D] = 0x94,
      [0x2022] = 0x95,
      [0x2013] = 0x96,
      [0x2014] = 0x97,
      [0x2122] = 0x99,
      [0x203A] = 0x9B,
      [0x00A0] = 0xA0,
      [0x0385] = 0xA1,
      [0x0386] = 0xA2,
      [0x00A3] = 0xA3,
      [0x00A4] = 0xA4,
      [0x00A5] = 0xA5,
      [0x00A6] = 0xA6,
      [0x00A7] = 0xA7,
      [0x00A8] = 0xA8,
      [0x00A9] = 0xA9,
      [0x00AB] = 0xAB,
      [0x00AC] = 0xAC,
      [0x00AD] = 0xAD,
      [0x00AE] = 0xAE,
      [0x2015] = 0xAF,
      [0x00B0] = 0xB0,
      [0x00B1] = 0xB1,
      [0x00B2] = 0xB2,
      [0x00B3] = 0xB3,
      [0x0384] = 0xB4,
      [0x00B5] = 0xB5,
      [0x00B6] = 0xB6,
      [0x00B7] = 0xB7,
      [0x0388] = 0xB8,
      [0x0389] = 0xB9,
      [0x038A] = 0xBA,
      [0x00BB] = 0xBB,
      [0x038C] = 0xBC,
      [0x00BD] = 0xBD,
      [0x038E] = 0xBE,
      [0x038F] = 0xBF,
      [0x0390] = 0xC0,
      [0x0391] = 0xC1,
      [0x0392] = 0xC2,
      [0x0393] = 0xC3,
      [0x0394] = 0xC4,
      [0x0395] = 0xC5,
      [0x0396] = 0xC6,
      [0x0397] = 0xC7,
      [0x0398] = 0xC8,
      [0x0399] = 0xC9,
      [0x039A] = 0xCA,
      [0x039B] = 0xCB,
      [0x039C] = 0xCC,
      [0x039D] = 0xCD,
      [0x039E] = 0xCE,
      [0x039F] = 0xCF,
      [0x03A0] = 0xD0,
      [0x03A1] = 0xD1,
      [0x03A3] = 0xD3,
      [0x03A4] = 0xD4,
      [0x03A5] = 0xD5,
      [0x03A6] = 0xD6,
      [0x03A7] = 0xD7,
      [0x03A8] = 0xD8,
      [0x03A9] = 0xD9,
      [0x03AA] = 0xDA,
      [0x03AB] = 0xDB,
      [0x03AC] = 0xDC,
      [0x03AD] = 0xDD,
      [0x03AE] = 0xDE,
      [0x03AF] = 0xDF,
      [0x03B0] = 0xE0,
      [0x03B1] = 0xE1,
      [0x03B2] = 0xE2,
      [0x03B3] = 0xE3,
      [0x03B4] = 0xE4,
      [0x03B5] = 0xE5,
      [0x03B6] = 0xE6,
      [0x03B7] = 0xE7,
      [0x03B8] = 0xE8,
      [0x03B9] = 0xE9,
      [0x03BA] = 0xEA,
      [0x03BB] = 0xEB,
      [0x03BC] = 0xEC,
      [0x03BD] = 0xED,
      [0x03BE] = 0xEE,
      [0x03BF] = 0xEF,
      [0x03C0] = 0xF0,
      [0x03C1] = 0xF1,
      [0x03C2] = 0xF2,
      [0x03C3] = 0xF3,
      [0x03C4] = 0xF4,
      [0x03C5] = 0xF5,
      [0x03C6] = 0xF6,
      [0x03C7] = 0xF7,
      [0x03C8] = 0xF8,
      [0x03C9] = 0xF9,
      [0x03CA] = 0xFA,
      [0x03CB] = 0xFB,
      [0x03CC] = 0xFC,
      [0x03CD] = 0xFD,
      [0x03CE] = 0xFE,
   }
   local char, byte, table_insert, table_concat = string.char, string.byte, table.insert, table.concat

   local function utf8_to_unicode(utf8str, pos)
      -- pos = starting byte position inside input string (default 1)
      pos = pos or 1
      local code, size = byte(utf8str, pos), 1
      if code >= 0xC0 and code < 0xFE then
         local mask = 64
         code = code - 128
         repeat
            local next_byte = byte(utf8str, pos + size) or 0
            if next_byte >= 0x80 and next_byte < 0xC0 then
               code, size = (code - mask - 2) * 64 + next_byte, size + 1
            else
               code, size = byte(utf8str, pos), 1
            end
            mask = mask * 32
         until code < mask
      end
      -- returns code, number of bytes in this utf8 char
      return code, size
   end

   local function utf8_to_1253(utf8str)
      local pos, result_1253 = 1, {}
      while pos <= #utf8str do
         local code, size = utf8_to_unicode(utf8str, pos)
         pos = pos + size
         code = code < 128 and code or map_unicode_to_1253[code] or byte('?')
         table_insert(result_1253, char(code))
      end
      return table_concat(result_1253)
   end

   local orig_os_rename = os.rename

   function os.rename(old, new)
      return orig_os_rename(utf8_to_1253(old), utf8_to_1253(new))
   end

   local orig_os_remove = os.remove

   function os.remove(filename)
      return orig_os_remove(utf8_to_1253(filename))
   end

   local orig_os_execute = os.execute

   function os.execute(command)
      if command then
         command = utf8_to_1253(command)
      end
      return orig_os_execute(command)
   end

   local orig_io_open = io.open

   function io.open(filename, ...)
      return orig_io_open(utf8_to_1253(filename), ...)
   end

   local orig_io_popen = io.popen

   function io.popen(prog, ...)
      return orig_io_popen(utf8_to_1253(prog), ...)
   end

   local orig_io_lines = io.lines

   function io.lines(filename, ...)
      if filename then
         filename = utf8_to_1253(filename)
      end
      return orig_io_lines(filename, ...)
   end

end

更新
如何确定Windows代码页:

local function get_windows_ansi_codepage()
   local pipe = assert(io.popen[[reg query HKLM\SYSTEM\CurrentControlSet\Control\Nls\CodePage /v ACP]])
   local codepage = pipe:read"*a":match"%sACP%s+REG_SZ%s+(.-)%s*$"
   pipe:close()
   return codepage  -- returns string "1253"
end

答案 1 :(得分:2)

我还试图在Windows的未经修改的Lua 5.3中使用非ASCII文件名,但是它不起作用。我认为它需要Lua的修改版本。我的理解是Lua对文件名,命令和环境变量使用基本的C函数,但是Windows使用UTF-16编码,并且要求您对非ASCII文件名使用宽字符串(在Windows上是UTF-16)函数。 ,命令和环境变量。

我编译并试用了Lua的修改版,它可以很好地处理非ASCII文件名:lua-u8w。它使用各种函数的宽字符串版本来处理文件等,并将其从UTF-8转换为UTF-16并返回,以便您可以在Lua中使用UTF-8,而UTF-16用于处理Windows操作系统。系统。

答案 2 :(得分:2)

在标准实现中,os.rename调用C函数rename,该函数依次在Windows上转到CreateFileA。该函数仅适用于ANSI字符串,并且在内部将字符串从ANSI转换为Unicode。

这使用您的语言环境设置指定的系统ANSI代码页。如果您的系统设置为希腊语,则可能会使用代码页1253,该代码页定义了希腊字符,但其他代码页中的字符不可用。

我不确定,但是如果系统代码页设置为65001(UTF-8),则可能允许您使用UTF-8。还有一个名为AppLocale的应用程序,只能将其设置为特定的应用程序。

如果您可以使用外部库,则似乎some可以在系统API调用中使用宽字符。

答案 3 :(得分:2)

正如其他人指出的那样,您将无法使用Lua的普通版本,因为它使用的是CreateFileA,而不是此功能的unicode版本(CreateFileW)。如果可以加载外部模块,则可以使用winapi,因为它支持检索“短”文件名:

local ok, winapi = pcall(require, "winapi")
if ok then
  winapi.set_encoding(winapi.CP_UTF8)
  local shortpath = winapi.short_path(filepath)
  if shortpath ~= filepath then
    -- have the short path
  end
end

此代码应在所有平台上均有效(因为它将无法在不需要此转换的macOS和Linux上加载winapi)。如果短文件名不可用,并且仍然可以使用fsutil 8dot3name set DRIVE: 0命令在Windows中配置(每个驱动器),则转换可能仍然会失败。

如果通过转换同时运行源文件名和目标文件名,则重命名将起作用(并删除目标文件,因为它可能是通过short_file调用创建的)。

答案 4 :(得分:1)

Egor Skriptunoff的代码完全解决了我的问题。我略微修改了他的代码,以便可以插入其他映射表,并根据区域设置使用正确的映射。

谢谢大家的帮助! :)

;