对于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
答案 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中的任何计算都快。