2015-02-22 14 views
6

Więc mam poniższy kod, żeby podzielić ciąg między whitespaces:Lua: split ciąg słowami chyba cytowany

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

Rezultat:

I 
am 
'the 
text' 

ale muszę to zrobić:

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

Jak mogę to zrobić?

Edycja: tylko po to, aby uzupełnić pytanie, chodzi o przekazanie parametrów z programu do innego programu. Oto żądanie ściągania, które działam, obecnie w recenzji: https://github.com/mpv-player/mpv/pull/1619

Odpowiedz

6

Może być na to sposób z inteligentnym analizowaniem, ale alternatywnym sposobem może być śledzenie stanu prostego i scalanie fragmentów w oparciu o wykrywanie cytowane fragmenty. Coś jak to może działać:

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 

ten wypisze:

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

Updated obsługiwać mieszane i uciekł cytaty. Zaktualizowano, by usunąć cytaty. Zaktualizowano, aby obsługiwać cytowane słowa.

+0

wolałbym coś używając ciąg parsowania. W każdym razie, podczas gdy ja nie powiedziałem w poście, potrzebuję czegoś do pracy zarówno z pojedynczymi, jak i podwójnymi cytatami, ponieważ ideą tego kodu jest parsowanie parametrów z powłoki. – m45t3r

+0

Łatwo jest zaktualizować to rozwiązanie, aby działało z pojedynczymi i podwójnymi cudzysłowami; po prostu zastąp ''^'" '' [[^^["']]]' i '"' $ "' za pomocą '[[[''] $]]'. Możesz również sprawdzić, czy otwierający cytat pasuje do zamykającego. –

+0

Można to zrobić za pomocą analizowania ciągów, ale rozwiązanie może być bardziej złożone (a nie z jednym wyrażeniem, ponieważ wzory Lua nie są wystarczająco silne, aby wyrazić to, czego potrzebujesz). –

1

Spróbuj tego:

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 
+0

Naprawiono obsługę zarówno pojedynczych, jak i podwójnych cudzysłowów oraz pusty cytowany tekst. – lhf

1

Lua Wzory nie są silne, aby obsłużyć tego zadania prawidłowo. Oto rozwiązanie LPeg zaadaptowane z wersji Lua Lexer. Obsługuje zarówno pojedyncze, jak i podwójne cudzysłowy.

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 

wyjściowa:

This is a string 
another string 
these 
are 
words