Dividir string em Lua?

160

Preciso fazer uma divisão simples de uma string, mas não parece haver uma função para isso, e a maneira manual que testei não parecia funcionar. Como eu faria isso?

RCIX
fonte
Por favor, veja Splitting Strings
Andrew Hare

Respostas:

96

Aqui está minha solução realmente simples. Use a função gmatch para capturar cadeias que contenham pelo menos um caractere de outra coisa que não seja o separador desejado. O separador é ** qualquer * espaço em branco (% s em Lua) por padrão:

function mysplit (inputstr, sep)
        if sep == nil then
                sep = "%s"
        end
        local t={}
        for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
                table.insert(t, str)
        end
        return t
end

.

Adrian Mole
fonte
1
Obrigado. Apenas o que eu estava procurando.
31412 Nicholas
3
Uau, a primeira resposta em toda essa pergunta que realmente tem uma função que retorna uma tabela. Note, no entanto, que eu preciso do modificador "local", pois você está substituindo os globais. :)
cib
3
Como outros já apontaram, você pode simplificar isso usando table.insert (t, str) em vez de t [i] = str e então você não precisa de i = 1 ou i = i +1
James Newton,
2
Não funciona se a string contiver valores vazios, por exemplo. 'foo,,bar'. Você obtém, em {'foo','bar'}vez de{'foo', '', 'bar'}
andras
5
Está certo. A próxima versão funcionará nesse caso: function split(inputstr, sep) sep=sep or '%s' local t={} for field,s in string.gmatch(inputstr, "([^"..sep.."]*)("..sep.."?)") do table.insert(t,field) if s=="" then return t end end end
bart
33

Se você estiver dividindo uma string em Lua, tente os métodos string.gmatch () ou string.sub (). Use o método string.sub () se você souber o índice no qual deseja dividir a string ou use o string.gmatch () se você analisará a string para encontrar o local em que ela será dividida.

Exemplo usando string.gmatch () do Manual de referência do Lua 5.1 :

 t = {}
 s = "from=world, to=Lua"
 for k, v in string.gmatch(s, "(%w+)=(%w+)") do
   t[k] = v
 end
gwell
fonte
I "emprestado" uma implementação a partir dessa página os usuários de lua obrigado de qualquer maneira
RCIX
24

Se você deseja apenas iterar sobre os tokens, isso é bastante interessante:

line = "one, two and 3!"

for token in string.gmatch(line, "[^%s]+") do
   print(token)
end

Resultado:

1,

dois

e

3!

Breve explicação: o padrão "[^% s] +" corresponde a todas as seqüências não vazias entre os caracteres de espaço.

Hugo
fonte
2
O padrão %Sé igual ao que você mencionou, assim como %Sa negação de %s, assim como %Da negação de %d. Além disso, %wé igual a [A-Za-z0-9_](outros caracteres podem ser suportados dependendo do seu código do idioma).
Lars Gyrup Brink Nielsen
14

Assim como string.gmatchencontrará padrões em uma string, esta função encontrará as coisas entre os padrões:

function string:split(pat)
  pat = pat or '%s+'
  local st, g = 1, self:gmatch("()("..pat..")")
  local function getter(segs, seps, sep, cap1, ...)
    st = sep and seps + #sep
    return self:sub(segs, (seps or 0) - 1), cap1 or sep, ...
  end
  return function() if st then return getter(st, g()) end end
end

Por padrão, ele retorna o que estiver separado por espaços em branco.

Norman Ramsey
fonte
6
+1. Nota para qualquer outro iniciante do Lua: isso retorna um iterador e 'entre padrões' inclui o início e o fim da string. (Como um novato eu tinha que experimentar para descobrir isso.)
Darius Bacon
12

Aqui está a função:

function split(pString, pPattern)
   local Table = {}  -- NOTE: use {n = 0} in Lua-5.0
   local fpat = "(.-)" .. pPattern
   local last_end = 1
   local s, e, cap = pString:find(fpat, 1)
   while s do
      if s ~= 1 or cap ~= "" then
     table.insert(Table,cap)
      end
      last_end = e+1
      s, e, cap = pString:find(fpat, last_end)
   end
   if last_end <= #pString then
      cap = pString:sub(last_end)
      table.insert(Table, cap)
   end
   return Table
end

Chame assim:

list=split(string_to_split,pattern_to_match)

por exemplo:

list=split("1:2:3:4","\:")


Para mais informações, acesse:
http://lua-users.org/wiki/SplitJoin

Faisal Hanif
fonte
7

Eu gosto desta solução curta

function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match);
    end
    return result;
end
Ivo Beckers
fonte
Este é o meu favorito, pois é muito curto e simples. Eu não entendo direito o que acontece, alguém poderia me explicar?
Hexagonest
2
Esta falha ao usar ponto como delimitador (ou potencialmente qualquer outro personagem padrão de mágica)
TurboHz
6

Como há mais de uma maneira de esfolar um gato, eis a minha abordagem:

Código :

#!/usr/bin/env lua

local content = [=[
Lorem ipsum dolor sit amet, consectetur adipisicing elit,
sed do eiusmod tempor incididunt ut labore et dolore magna 
aliqua. Ut enim ad minim veniam, quis nostrud exercitation 
ullamco laboris nisi ut aliquip ex ea commodo consequat.
]=]

local function split(str, sep)
   local result = {}
   local regex = ("([^%s]+)"):format(sep)
   for each in str:gmatch(regex) do
      table.insert(result, each)
   end
   return result
end

local lines = split(content, "\n")
for _,line in ipairs(lines) do
   print(line)
end

Saída : Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.

Explicação :

A gmatchfunção funciona como um iterador, busca todas as strings correspondentes regex. A regextoma todos os caracteres até encontrar um separador.

Diego Pino
fonte
5

Você pode usar este método:

function string:split(delimiter)
  local result = { }
  local from  = 1
  local delim_from, delim_to = string.find( self, delimiter, from  )
  while delim_from do
    table.insert( result, string.sub( self, from , delim_from-1 ) )
    from  = delim_to + 1
    delim_from, delim_to = string.find( self, delimiter, from  )
  end
  table.insert( result, string.sub( self, from  ) )
  return result
end

delimiter = string.split(stringtodelimite,pattern) 
krsk9999
fonte
5

Muitas dessas respostas aceitam apenas separadores de caractere único ou não lidam bem com casos extremos (por exemplo, separadores vazios); portanto, pensei em fornecer uma solução mais definitiva.

Aqui estão duas funções gsplite split, adaptadas do código na extensão Scribunto MediaWiki , que é usada em wikis como a Wikipedia. O código é licenciado sob a GPL v2 . Alterei os nomes das variáveis ​​e adicionei comentários para tornar o código um pouco mais fácil de entender, e também alterei o código para usar padrões regulares de strings Lua em vez dos padrões de Scribunto para strings Unicode. O código original tem casos de teste aqui .

-- gsplit: iterate over substrings in a string separated by a pattern
-- 
-- Parameters:
-- text (string)    - the string to iterate over
-- pattern (string) - the separator pattern
-- plain (boolean)  - if true (or truthy), pattern is interpreted as a plain
--                    string, not a Lua pattern
-- 
-- Returns: iterator
--
-- Usage:
-- for substr in gsplit(text, pattern, plain) do
--   doSomething(substr)
-- end
local function gsplit(text, pattern, plain)
  local splitStart, length = 1, #text
  return function ()
    if splitStart then
      local sepStart, sepEnd = string.find(text, pattern, splitStart, plain)
      local ret
      if not sepStart then
        ret = string.sub(text, splitStart)
        splitStart = nil
      elseif sepEnd < sepStart then
        -- Empty separator!
        ret = string.sub(text, splitStart, sepStart)
        if sepStart < length then
          splitStart = sepStart + 1
        else
          splitStart = nil
        end
      else
        ret = sepStart > splitStart and string.sub(text, splitStart, sepStart - 1) or ''
        splitStart = sepEnd + 1
      end
      return ret
    end
  end
end

-- split: split a string into substrings separated by a pattern.
-- 
-- Parameters:
-- text (string)    - the string to iterate over
-- pattern (string) - the separator pattern
-- plain (boolean)  - if true (or truthy), pattern is interpreted as a plain
--                    string, not a Lua pattern
-- 
-- Returns: table (a sequence table containing the substrings)
local function split(text, pattern, plain)
  local ret = {}
  for match in gsplit(text, pattern, plain) do
    table.insert(ret, match)
  end
  return ret
end

Alguns exemplos da splitfunção em uso:

local function printSequence(t)
  print(unpack(t))
end

printSequence(split('foo, bar,baz', ',%s*'))       -- foo     bar     baz
printSequence(split('foo, bar,baz', ',%s*', true)) -- foo, bar,baz
printSequence(split('foo', ''))                    -- f       o       o
Jack Taylor
fonte
5

um caminho não visto nos outros

function str_split(str, sep)
    if sep == nil then
        sep = '%s'
    end 

    local res = {}
    local func = function(w)
        table.insert(res, w)
    end 

    string.gsub(str, '[^'..sep..']+', func)
    return res 
end
Hohenheim
fonte
4

Simplesmente sentado em um delimitador

local str = 'one,two'
local regxEverythingExceptComma = '([^,]+)'
for x in string.gmatch(str, regxEverythingExceptComma) do
    print(x)
end
Jerome Anthony
fonte
3

Eu usei os exemplos acima para criar minha própria função. Mas a peça que faltava para mim estava escapando automaticamente dos personagens mágicos.

Aqui está a minha contribuição:

function split(text, delim)
    -- returns an array of fields based on text and delimiter (one character only)
    local result = {}
    local magic = "().%+-*?[]^$"

    if delim == nil then
        delim = "%s"
    elseif string.find(delim, magic, 1, true) then
        -- escape magic
        delim = "%"..delim
    end

    local pattern = "[^"..delim.."]+"
    for w in string.gmatch(text, pattern) do
        table.insert(result, w)
    end
    return result
end
intrépido herói
fonte
Este também foi o meu grande problema. Isso funciona muito bem com personagens mágicos, nice one
Andrew White
1

Você poderia usar a biblioteca do penlight . Isso tem uma função para dividir string usando o delimitador que gera a lista.

Ele implementou muitas das funções que podemos precisar durante a programação e a falta em Lua.

Aqui está a amostra para usá-lo.

> 
> stringx = require "pl.stringx"
> 
> str = "welcome to the world of lua"
> 
> arr = stringx.split(str, " ")
> 
> arr
{welcome,to,the,world,of,lua}
> 

fonte
0

Dependendo do caso de uso, isso pode ser útil. Corta todo o texto em ambos os lados das bandeiras:

b = "This is a string used for testing"

--Removes unwanted text
c = (b:match("a([^/]+)used"))

print (c)

Resultado:

string
vegetação
fonte
0

É tarde demais para esta pergunta, mas caso alguém queira uma versão que lide com a quantidade de divisões que você deseja obter .....

-- Split a string into a table using a delimiter and a limit
string.split = function(str, pat, limit)
  local t = {}
  local fpat = "(.-)" .. pat
  local last_end = 1
  local s, e, cap = str:find(fpat, 1)
  while s do
    if s ~= 1 or cap ~= "" then
      table.insert(t, cap)
    end

    last_end = e+1
    s, e, cap = str:find(fpat, last_end)

    if limit ~= nil and limit <= #t then
      break
    end
  end

  if last_end <= #str then
    cap = str:sub(last_end)
    table.insert(t, cap)
  end

  return t
end
Benjamin Vison
fonte
0

Se você programa em Lua, está sem sorte aqui. Lua é A única linguagem de programação que é notoriamente infame porque seus autores nunca implementaram "a" função de divisão na biblioteca padrão e, em vez disso, escreveram 16 tela cheia de explicações e desculpas esfarrapadas de por que eles não fizeram e não o fizeram, intercalado com vários exemplos de semi-trabalho que são praticamente garantidos para trabalhar para quase todos, exceto na sua área de trabalho . Este é apenas o estado da arte de Lua, e todo mundo que programa em Lua simplesmente acaba cerrando os dentes e iterando sobre os personagens. Existem muitas soluções existentes às vezes melhores, mas exatamente zero soluções confiáveis .

Szczepan Hołyszewski
fonte