更优雅,更简单的方法将代码转换为UTF-8

时间:2014-09-27 03:48:05

标签: utf-8 lua

对于this question,我创建了以下Lua代码,用于将Unicode代码点转换为UTF-8字符串。有没有更好的方法(在Lua 5.1+中)?在这种情况下,“更好”意味着“效率更高,或者更好 - 更少的代码行”

注意:我并不是真的要求code review这个算法;我要求更好的算法(或内置库)。

do
  local bytebits = {
    {0x7F,{0,128}},
    {0x7FF,{192,32},{128,64}},
    {0xFFFF,{224,16},{128,64},{128,64}},
    {0x1FFFFF,{240,8},{128,64},{128,64},{128,64}}
  }
  function utf8(decimal)
    local charbytes = {}
    for b,lim in ipairs(bytebits) do
      if decimal<=lim[1] then
        for i=b,1,-1 do
          local prefix,max = lim[i+1][1],lim[i+1][2]
          local mod = decimal % max
          charbytes[i] = string.char( prefix + mod )
          decimal = ( decimal - mod ) / max
        end
        break
      end
    end
    return table.concat(charbytes)
  end
end

c=utf8(0x24)     print(c.." is "..#c.." bytes.") --> $ is 1 bytes.
c=utf8(0xA2)     print(c.." is "..#c.." bytes.") --> ¢ is 2 bytes.
c=utf8(0x20AC)   print(c.." is "..#c.." bytes.") --> € is 3 bytes.  
c=utf8(0xFFFF)   print(c.." is "..#c.." bytes.") -->  is 3 bytes.
c=utf8(0x10000)  print(c.." is "..#c.." bytes.") -->  is 4 bytes.
c=utf8(0x24B62)  print(c.." is "..#c.." bytes.") -->  is 4 bytes.   

我觉得应该有办法摆脱整个bytebits预定义表并循环以找到匹配的条目。从后面循环我可以继续%64并添加128以形成延续字节,直到值低于128,但我无法弄清楚如何优雅地生成0 / {要添加的{1}} / 110 / 1110序言。


编辑:这是一个稍微好一点的返工,速度优化。然而,这不是一个可接受的答案,因为算法仍然基本上是相同的想法和相同数量的代码。

11110

2 个答案:

答案 0 :(得分:3)

Lua 5.3提供a basic UTF-8 library,其中函数utf8.char正是您所寻找的:

  

接收零个或多个整数,将每个整数转换为其对应的UTF-8字节序列,并返回一个字符串,其中包含所有这些序列的串联。

c = utf8.char(0x24)     print(c.." is "..#c.." bytes.") --> $ is 1 bytes.
c = utf8.char(0xA2)     print(c.." is "..#c.." bytes.") --> ¢ is 2 bytes.
c = utf8.char(0x20AC)   print(c.." is "..#c.." bytes.") --> € is 3 bytes.  
c = utf8.char(0xFFFF)   print(c.." is "..#c.." bytes.") -->  is 3 bytes.
c = utf8.char(0x10000)  print(c.." is "..#c.." bytes.") -->  is 4 bytes.
c = utf8.char(0x24B62)  print(c.." is "..#c.." bytes.") -->  is 4 bytes.

答案 1 :(得分:3)

如果我们谈论速度,那么现实世界中的使用模式非常重要。但在这里,我们处于真空状态,所以无论如何都要继续。

当你说你应该能够摆脱 bytebits 时,你可能正在寻找这个算法:

do
  local string_char = string.char
  function utf8(cp)
    if cp < 128 then
      return string_char(cp)
    end
    local s = ""
    local prefix_max = 32
    while true do
      local suffix = cp % 64
      s = string_char(128 + suffix)..s
      cp = (cp - suffix) / 64
      if cp < prefix_max then
        return string_char((256 - (2 * prefix_max)) + cp)..s
      end
      prefix_max = prefix_max / 2
    end
  end
end

它还包括一些其他特别有趣的优化,对我而言,它的优化速度是优化的给定代码的2倍。 (作为奖励,它应该一直工作到U + 7FFFFFFF。)

如果我们想要进一步微量优化,可以将循环展开到:

do
  local string_char = string.char
  function utf8_unrolled(cp)
    if cp < 128 then
      return string_char(cp)
    end
    local suffix = cp % 64
    local c4 = 128 + suffix
    cp = (cp - suffix) / 64
    if cp < 32 then
      return string_char(192 + cp, c4)
    end
    suffix = cp % 64
    local c3 = 128 + suffix
    cp = (cp - suffix) / 64
    if cp < 16 then
      return string_char(224 + cp, c3, c4)
    end
    suffix = cp % 64
    cp = (cp - suffix) / 64
    return string_char(240 + cp, 128 + suffix, c3, c4)
  end
end

这大约是优化代码的5倍,但完全不优雅。我认为主要的好处是不必将中间结果存储在堆上并且具有较少的函数调用。

然而,最快的(据我所知)方法根本不是进行计算:

do
  local lookup = {}
  for i=0,0x1FFFFF do
    lookup[i]=calculate_utf8(i)
  end  
  function utf8(cp)
    return lookup[cp]
  end
end

这大约是优化代码的30倍,可能符合以下条件:&#34;效率更高&#34; (虽然内存使用很荒谬)。但是,它也没有意思。 (在某些情况下,一个很好的折衷方案是使用memoization。)

当然,任何纯c实现都可能比Lua中的任何计算都快。