Split string en Lua?

160

Necesito hacer una división simple de una cadena, pero no parece haber una función para esto, y la forma manual que probé no pareció funcionar. ¿Cómo lo haría?

RCIX
fuente
Ver Splitting Strings
Andrew Hare el

Respuestas:

96

Aquí está mi solución realmente simple. Use la función gmatch para capturar cadenas que contengan al menos un carácter de cualquier cosa que no sea el separador deseado. El separador es ** cualquier * espacio en blanco (% s en Lua) por defecto:

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
fuente
1
Gracias. Justo lo que estaba buscando.
Nicholas
3
Wow, la primera respuesta en toda esta pregunta que realmente tiene una función que devuelve una tabla. Sin embargo, tenga en cuenta que t y yo necesitamos el modificador "local", ya que está sobrescribiendo globales. :)
cib
3
Como otros han señalado, puede simplificar esto usando table.insert (t, str) en lugar de t [i] = str y luego no necesita i = 1 o i = i +1
James Newton
2
No funciona si la cadena contiene valores vacíos, por ejemplo. 'foo,,bar'. Usted obtiene en {'foo','bar'}lugar de{'foo', '', 'bar'}
andras
55
Así es. La siguiente versión funcionará en ese 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

Si está dividiendo una cadena en Lua, debe probar los métodos string.gmatch () o string.sub (). Use el método string.sub () si conoce el índice en el que desea dividir la cadena, o use string.gmatch () si analizará la cadena para encontrar la ubicación en la que dividir la cadena.

Ejemplo usando string.gmatch () del Manual de referencia de Lua 5.1 :

 t = {}
 s = "from=world, to=Lua"
 for k, v in string.gmatch(s, "(%w+)=(%w+)") do
   t[k] = v
 end
bien
fuente
I "prestada" una puesta en práctica de esa página los usuarios lua gracias de todos modos
RCIX
24

Si solo desea iterar sobre los tokens, esto es bastante bueno:

line = "one, two and 3!"

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

Salida:

uno,

dos

y

3!

Breve explicación: el patrón "[^% s] +" coincide con cada cadena no vacía entre caracteres de espacio.

Hugo
fuente
2
El patrón %Ses igual al que usted mencionó, como lo %Ses la negación de %s, como %Des la negación de %d. Además, %wes igual a [A-Za-z0-9_](es posible que se admitan otros caracteres según su entorno local).
Lars Gyrup Brink Nielsen
14

Así como string.gmatchencontrará patrones en una cadena, esta función encontrará las cosas entre patrones:

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 defecto, devuelve lo que esté separado por espacios en blanco.

Norman Ramsey
fuente
66
+1. Nota para cualquier otro principiante de Lua: esto devuelve un iterador, y 'entre patrones' incluye el comienzo y el final de la cadena. (Como novato tuve que intentarlo para resolver estas cosas.)
Darius Bacon
12

Aquí está la función:

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

Llámalo como:

list=split(string_to_split,pattern_to_match)

p.ej:

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


Para más información, visite aquí:
http://lua-users.org/wiki/SplitJoin

Faisal Hanif
fuente
7

Me gusta esta solucion corta

function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match);
    end
    return result;
end
Ivo Beckers
fuente
Este es mi favorito, ya que es muy corto y simple. No entiendo muy bien lo que sucede, ¿podría alguien explicarme?
hexagonest
2
Esto falla cuando se utiliza el punto como delimitador (o potencialmente cualquier otro personaje mágico de patrón)
TurboHz
6

Debido a que hay más de una forma de pelar un gato, este es mi enfoque:

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

Salida : 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.

Explicacion :

La gmatchfunción funciona como un iterador, recupera todas las cadenas que coinciden regex. El regextoma todos los caracteres hasta que encuentra un separador.

Diego Pino
fuente
5

Puedes 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
fuente
5

Muchas de estas respuestas solo aceptan separadores de un solo carácter, o no tratan bien los casos límite (por ejemplo, separadores vacíos), por lo que pensé que proporcionaría una solución más definitiva.

Aquí hay dos funciones gsplity split, adaptadas del código en la extensión Scribunto MediaWiki , que se usa en wikis como Wikipedia. El código tiene licencia bajo la GPL v2 . Cambié los nombres de las variables y agregué comentarios para que el código sea un poco más fácil de entender, y también cambié el código para usar patrones de cadena Lua regulares en lugar de los patrones de Scribunto para cadenas Unicode. El código original tiene casos de prueba aquí .

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

Algunos ejemplos de la splitfunción en 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
fuente
5

una forma no vista en otros

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
fuente
4

Simplemente sentado en un delimitador

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

Usé los ejemplos anteriores para diseñar mi propia función. Pero la pieza que faltaba para mí era escapar automáticamente de los personajes mágicos.

Aquí está mi contribución:

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
fuente
Este fue mi gran problema también. Esto funciona muy bien con personajes mágicos, uno agradable
Andrew White
1

Podrías usar la biblioteca penlight . Esto tiene una función para dividir cadenas usando delimitador que genera la lista.

Ha implementado muchas de las funciones que podemos necesitar al programar y faltar en Lua.

Aquí está la muestra para usarlo.

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

fuente
0

Dependiendo del caso de uso, esto podría ser útil. Corta todo el texto a ambos lados de las banderas:

b = "This is a string used for testing"

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

print (c)

Salida:

string
greenage
fuente
0

Súper tarde a esta pregunta, pero en caso de que alguien quiera una versión que maneje la cantidad de divisiones que desea obtener .....

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

Si programa en Lua, no tiene suerte aquí. Lua es EL único lenguaje de programación que resulta notoriamente infame porque sus autores nunca implementaron "la" función dividida en la biblioteca estándar, y en su lugar escribieron 16 pantallas llenas de explicaciones y excusas poco convincentes sobre por qué no lo hicieron y no lo harían, intercalado con numerosos ejemplos de medio trabajo que están prácticamente garantizados para trabajar para casi todos, pero que se rompen en su caso de esquina. Esto es solo el estado del arte de Lua, y todos los que programan en Lua simplemente terminan apretando los dientes e iterando sobre los personajes. Existen muchas soluciones que a veces son mejores, pero exactamente cero soluciones que son confiablemente mejores.

Szczepan Hołyszewski
fuente