Lua:将字符串拆分为单词,除非在引号内

6

我有以下代码用于在空格之间拆分字符串:

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
3个回答

8

可能有一些巧妙的解析方法,但另一种方法是保持简单状态并根据引用片段的检测合并片段。可以尝试以下方式:

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中解析参数。 - m45t3r
更新此解决方案以使其与单引号和双引号一起使用非常容易;只需将"^"替换为[[^["']]],将"'$"替换为[[['"]$]]`即可。您还需要检查开头引号是否与结尾引号匹配。 - Paul Kulchenko
可以通过字符串解析来实现,但解决方案可能会更加复杂(而且不是使用一个表达式,因为Lua模式不足以表达您所需的内容)。 - Paul Kulchenko
@m45t3r,我更新了代码以处理混合和转义引号。 - Paul Kulchenko
好的,我们用另一种方式解决了这个问题(使用mpv的内部键值对表示法而不是传递字符串),但我很喜欢你的答案(因为它不需要另一个库,代码比其他非库答案更清晰),所以我将其标记为答案。 - m45t3r

1

试试这个:

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

修复了处理单引号、双引号和空引号文本的问题。 - lhf

1
Lua模式无法很好地处理这个任务。这里提供了一个LPeg解决方案,改编自Lua Lexer。它可以处理单引号和双引号。
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接