Lua 使用LPeg编辑文本

Lua 使用LPeg编辑文本,lua,Lua,说来话长,但我会尽量保持简短。我有许多纯文本段落,我从系统中提取并以wiki格式重新输出这些段落,因此复制上述数据并不是一项艰巨的任务。这一切都进行得非常顺利,只是没有为我们有页面的“主题”生成自动引用,最终需要通过阅读所有文本并通过将主题更改为[[Topic]]手动添加来添加 第一个要求:每个主题只能点击一次,这是第一次出现。否则,它将成为一个真正的垃圾链接,这将降低可读性。避免以相同单词开头的主题出现问题 第二个要求:重叠主题名称的处理方式应确保最“精确”的主题获得链接,在以后出现的情况下,

说来话长,但我会尽量保持简短。我有许多纯文本段落,我从系统中提取并以wiki格式重新输出这些段落,因此复制上述数据并不是一项艰巨的任务。这一切都进行得非常顺利,只是没有为我们有页面的“主题”生成自动引用,最终需要通过阅读所有文本并通过将主题更改为[[Topic]]手动添加来添加

第一个要求:每个主题只能点击一次,这是第一次出现。否则,它将成为一个真正的垃圾链接,这将降低可读性。避免以相同单词开头的主题出现问题

第二个要求:重叠主题名称的处理方式应确保最“精确”的主题获得链接,在以后出现的情况下,不太精确的主题不会获得链接,因为它们可能不正确

例如:

topics = { "Project", "Mary", "Mr. Moore", "Project Omega"}
input = "Mary and Mr. Moore work together on Project Omega. Mr. Moore hates both Mary and Project Omega, but Mary simply loves the Project."
output = function_to_be_written(input)
-- "[[Mary]] and [[Mr. Moore]] work together on [[Project Omega]]. Mr. Moore hates both Mary and Project Omega, but Mary simply loves the [[Project]]."
现在,我很快发现了一个简单或复杂的字符串。gsub()无法满足第二个要求,因为它无法表示“将此匹配视为未发生-我希望您进一步回溯”。我需要引擎做一些类似于:

input = "abc def ghi"
-- Looping over the input would, in this order, match the following strings:
-- 1) abc def ghi
-- 2) abc def
-- 3) abc
-- 4) def ghi
-- 5) def
-- 6) ghi
一旦字符串与实际主题匹配,并且以前没有被其维基化版本替换,它就会被替换。如果此主题以前已被wikified版本替换,请不要替换,只需在主题末尾继续匹配即可。(因此,对于主题“abc def”,接下来将在这两种情况下测试“ghi”。)

于是我来到了LPeg。我读过它,玩过它,但它相当复杂,虽然我认为我需要使用lpeg.Cmtlpeg.Cs,但不知何故,我无法正确地将两者混合在一起,以使我想做的事情起作用。我不想发布我的练习尝试,因为它们的质量很差,可能更容易迷惑任何人,而不是帮助澄清我的问题


(为什么我想使用PEG而不是自己编写三重嵌套循环?因为我不想,这是学习PEG的一个很好的借口..只是我有点不知所措。除非LPeg不可能,否则第一个选项不是选项。)

那么为什么不使用string.find?它只搜索第一次出现的主题,并提供其起始索引和长度。您所要做的就是在结果中添加“[[]”。 对于每个区块,复制topics表,并在找到第一个出现时将其删除。 按长度排序主题,最长优先,以便首先找到最相关的主题


LPeg是一个很好的工具,但在这里没有必要使用它。

所以…我觉得很无聊,需要做点什么:

topics = { "Project", "Mary", "Mr. Moore", "Project Omega"}

pcall ( require , 'luarocks.require' )
require 'lpeg'
local locale = lpeg.locale ( )
local endofstring = -lpeg.P(1)
local endoftoken = (locale.space+locale.punct)^1

table.sort ( topics , function ( a , b ) return #a > #b end ) -- Sort by word length (longest first)
local topicpattern = lpeg.P ( false )
for i = 1, #topics do
    topicpattern = topicpattern + topics [ i ]
end

function wikify ( input )
    local topicsleft = { }
    for i = 1 , #topics do
        topicsleft [ topics [ i ] ] = true
    end

    local makelink = function ( topic )
        if topicsleft [ topic ] then
            topicsleft [ topic ] = nil
            return "[[" .. topic .. "]]"
        else
            return topic
        end
    end

    local patt = lpeg.Ct ( 
        (
            lpeg.Cs ( ( topicpattern / makelink ) )* #(-locale.alnum+endofstring) -- Match topics followed by something thats not alphanumeric
            + lpeg.C ( ( lpeg.P ( 1 ) - endoftoken )^0 * endoftoken ) -- Skip tokens that aren't topics
        )^0 * endofstring -- Match adfinum until end of string
    )
    return table.concat ( patt:match ( input ) )
end

print(wikify("Mary and Mr. Moore work together on Project Omega. Mr. Moore hates both Mary and Project Omega, but Mary simply loves the Project.")..'"')
print(wikify("Mary and Mr. Moore work on Project Omegality. Mr. Moore hates Mary and Project Omega, but Mary loves the Projectaaa.")..'"')
我首先创建一个匹配所有不同主题的模式;我们希望首先匹配最长的主题,因此按单词长度从最长到最短对表格进行排序。 现在我们需要列出在当前输入中没有看到的主题。 makelink引用/链接主题,如果我们还没有看到它,否则就离开它

现在来看看lpeg的实际内容:

  • lpeg.Ct
    将我们所有的捕获打包到一个表中(集中在一起输出)
  • topicpattern/makelink
    捕获一个主题,并通过makelink函数传入
  • lpeg.Cs
    将makelink的结果替换回主题匹配的位置
  • +lpeg.C((lpeg.P(1)-locale.space)^0*locale.space^1)
    如果主题不匹配,请跳过一个单词(即,不要在空格后加空格)
  • ^0
    重复
希望这就是你想要的:)

道恩


注意:编辑过的代码,描述不再正确

是的,我最终使用了一堆普通的循环。重点是我想学习LPEG,据我所知,它们能够匹配比简单正则表达式更广泛的模式。因此,当我发现正则表达式不容易做到的事情时,我想这是我实际使用LPeg解决实际问题的机会,而不仅仅是为了解决它。我知道一个有趣的项目,你可以帮助并立即学习LPeg。它有不完整的LPeg语法和完整的BNF语法。我在Erlang的实现中遇到了一些与空白相关的问题-Neotoma希望所有这些东西都能解决对你来说很有趣非常棒的努力,我学到了很多。另外,它是区分大小写的(我承认我在示例中没有特别提到或测试),但我认为通过以小写形式跟踪所有主题可以很容易地解决这个问题,尽管我认为lpeg.P()可能不同意我的观点。更大的问题是:它喜欢匹配部分单词,并且在我运行代码时,它会在最后一次匹配后删除数据。例如,尝试这个(修改后的)测试:打印(wikify(“玛丽和摩尔先生致力于Omegality项目。摩尔先生讨厌玛丽和Omega项目,但玛丽喜欢Projectaaa。”)它显示了这两个问题。这太棒了。一个真正的宝石。谢谢你的努力-我从你身上学到了很多。如果你还有机会,你能解释一下,例如,如何在不敏感的情况下匹配主题大小写吗?我正在想象预处理每个主题看起来像下面正则表达式的lpeg形式:'[Mm][Aa][Rr Yy]’,但不知何故,我认为这是一种错误的理解方式。--无论哪种方式,我都接受这个答案,因为我从中学到了很多东西,它满足了我的所有要求。谢谢!:)获得不区分大小写的最简单方法就是将所有内容都小写:主题列表,并在调用:match()时使用input:lower().是的。但我认为lpeg.P()区分大小写匹配?我不能将输入字符串小写(因为明显的原因是最终输出需要保持其正常大小写),因此这意味着它永远无法将实际数据中的主题“test”与单词“test”匹配?或者我对lpeg.P()的理解有什么错误吗?嗯