使用 LPeg 匹配 Unicode 标点符号

我正在尝试创建一个 LPeg 模式,用于匹配 UTF-8 编码输入中的任何 Unicode 标点符号。我想到了 Selene Unicode 和 LPeg 的以下配合:

local unicode     = require("unicode")
local lpeg        = require("lpeg")
local punctuation = lpeg.Cmt(lpeg.Cs(any * any^-3), function(s,i,a)
  local match = unicode.utf8.match(a, "^%p")
  if match == nil
    return false
  else
    return i+#match
  end
end)

这似乎有效,但它会错过由几个 Unicode 代码点组成的标点符号字符(如果存在这样的字符),因为我仅向前读取了 4 个字节,这可能会降低解析器的性能,并且不确定库 match 函数在我提供同时包含错误的 UTF-8 字符的字符串时会怎样运作(尽管它现在似乎可以用)。

我想知道这是不是一个正确的方法,或者是否有更好的方法来实现我想要实现的目标。

点赞
用户667959
用户667959

正确匹配UTF-8字符的方法在Lpeg主页中的示例中展示。UTF-8字符的第一个字节确定其是否有多个字节:

local cont = lpeg.R("\128\191") -- continuation byte

local utf8 = lpeg.R("\0\127")
           + lpeg.R("\194\223") * cont
           + lpeg.R("\224\239") * cont * cont
           + lpeg.R("\240\244") * cont * cont * cont

基于此utf8模式,我们可以使用lpeg.Cmt和Selene Unicode的match函数,就像您提出的那样:

local punctuation = lpeg.Cmt(lpeg.C(utf8), function (s, i, c)
    if unicode.utf8.match(c, "%p") then
        return i
    end
end)

请注意,我们返回i,这符合Cmt的预期:

给定的函数作为参数的主题,匹配patt之后的当前位置,以及patt产生的任何捕获值。函数返回的第一个值定义了匹配方式。如果调用返回一个数字,则匹配成功,并返回的数字成为新的当前位置。

这意味着我们应该返回函数接收的同一个数字,即UTF-8字符后面紧跟的位置。

2016-08-18 07:18:43