【问题标题】:Lua: split string into words unless quotedLua:除非引用,否则将字符串拆分为单词
【发布时间】:2015-04-24 04:58:37
【问题描述】:

所以我有以下代码在空格之间分割字符串:

text = "I am 'the text'"
for string in text:gmatch("%S+") do
    print(string)
end

结果:

I
am
'the
text'

但我需要这样做:

I
am
the text --[[yep, without the quotes]]

我该怎么做?

编辑:只是为了补充这个问题,这个想法是将参数从一个程序传递到另一个程序。这是我正在处理的拉取请求,目前正在审查中:https://github.com/mpv-player/mpv/pull/1619

【问题讨论】:

    标签: string lua lua-patterns


    【解决方案1】:

    可能有一些方法可以通过巧妙的解析来做到这一点,但另一种方法可能是跟踪简单状态并根据引用片段的检测合并片段。这样的事情可能会奏效:

    local text = [[I "am" 'the text' and "some more text with '" and "escaped \" text"]]
    local spat, epat, buf, quoted = [=[^(['"])]=], [=[(['"])$]=]
    for str in text:gmatch("%S+") do
      local squoted = str:match(spat)
      local equoted = str:match(epat)
      local escaped = str:match([=[(\*)['"]$]=])
      if squoted and not quoted and not equoted then
        buf, quoted = str, squoted
      elseif buf and equoted == quoted and #escaped % 2 == 0 then
        str, buf, quoted = buf .. ' ' .. str, nil, nil
      elseif buf then
        buf = buf .. ' ' .. str
      end
      if not buf then print((str:gsub(spat,""):gsub(epat,""))) end
    end
    if buf then print("Missing matching quote for "..buf) end
    

    这将打印:

    I
    am
    the text
    and
    some more text with '
    and
    escaped \" text
    

    已更新以处理混合和转义的引号。更新以删除引号。更新以处理引用的单词。

    【讨论】:

    • 我更喜欢使用字符串解析的东西。无论如何,虽然我没有在帖子中说我需要一些东西来处理单引号和双引号,因为这段代码的想法是从 shell 解析参数。
    • 很容易更新这个解决方案,使它可以使用单引号和双引号;只需将"^"` 替换为[[^["']]],将"'$" 替换为[[['"]$]]。您可能还需要检查开头引号是否与结尾引号匹配。
    • 可能与字符串解析有关,但解决方案可能更复杂(而不是使用一个表达式,因为 Lua 模式不足以表达您的需求)。
    • @m45t3r,我更新了处理混合引号和转义引号的代码。
    • 好吧,我们确实以另一种方式解决了问题(使用 mpv 的内部键值对表示而不是传递字符串),但我非常喜欢你的回答(因为它不需要另一个库和该代码比其他非库答案更干净),所以我将其标记为答案。
    【解决方案2】:

    试试这个:

    text = [[I am 'the text' and '' here is "another text in quotes" and this is the end]]
    
    local e = 0
    while true do
        local b = e+1
        b = text:find("%S",b)
        if b==nil then break end
        if text:sub(b,b)=="'" then
            e = text:find("'",b+1)
            b = b+1
        elseif text:sub(b,b)=='"' then
            e = text:find('"',b+1)
            b = b+1
        else
            e = text:find("%s",b+1)
        end
        if e==nil then e=#text+1 end
        print("["..text:sub(b,e-1).."]")
    end
    

    【讨论】:

    • 修复处理单引号和双引号,以及空引号文本。
    【解决方案3】:

    Lua 模式无法正确处理此任务。这是改编自Lua LexerLPeg 解决方案。它同时处理单引号和双引号。

    local lpeg = require 'lpeg'
    
    local P, S, C, Cc, Ct = lpeg.P, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Ct
    
    local function token(id, patt) return Ct(Cc(id) * C(patt)) end
    
    local singleq = P "'" * ((1 - S "'\r\n\f\\") + (P '\\' * 1)) ^ 0 * "'"
    local doubleq = P '"' * ((1 - S '"\r\n\f\\') + (P '\\' * 1)) ^ 0 * '"'
    
    local white = token('whitespace', S('\r\n\f\t ')^1)
    local word = token('word', (1 - S("' \r\n\f\t\""))^1)
    
    local string = token('string', singleq + doubleq)
    
    local tokens = Ct((string + white + word) ^ 0)
    
    
    input = [["This is a string" 'another string' these are words]]
    for _, tok in ipairs(lpeg.match(tokens, input)) do
      if tok[1] ~= "whitespace" then
         if tok[1] == "string" then
            print(tok[2]:sub(2,-2)) -- cut off quotes
         else
           print(tok[2])
         end
      end
    end
    

    输出:

    This is a string
    another string
    these
    are
    words
    

    【讨论】:

      猜你喜欢
      • 2013-09-13
      • 1970-01-01
      • 2011-06-12
      • 1970-01-01
      • 2014-06-09
      • 1970-01-01
      • 2022-01-18
      • 2011-10-23
      • 2011-11-03
      相关资源
      最近更新 更多