Podzielona struna w Lua?

160

Muszę zrobić prosty podział łańcucha, ale wygląda na to, że nie ma do tego funkcji, a ręczny sposób, który testowałem, nie działał. Jak bym to zrobił?

RCIX
źródło
Zobacz: Splitting Strings
Andrew Hare,

Odpowiedzi:

96

Oto moje naprawdę proste rozwiązanie. Użyj funkcji gmatch, aby przechwycić ciągi zawierające co najmniej jeden znak lub dowolny inny znak niż żądany separator. Separatorem jest domyślnie ** dowolna * biała spacja (% s w Lua):

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
źródło
1
Dzięki. Właśnie tego szukałem.
Nicholas
3
Wow, pierwsza odpowiedź w tym całym pytaniu, która faktycznie ma funkcję zwracającą tabelę. Zauważ jednak, że t i ja potrzebuję modyfikatora „local”, ponieważ nadpisujesz globals. :)
cib
3
Jak zauważyli inni, możesz to uprościć, używając table.insert (t, str) zamiast t [i] = str, a wtedy nie potrzebujesz i = 1 ani i = i +1
James Newton
2
Nie działa, jeśli łańcuch zawiera puste wartości, np. 'foo,,bar'. Dostajesz {'foo','bar'}zamiast{'foo', '', 'bar'}
andras
5
Zgadza się. Następna wersja będzie działać w takim przypadku: 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

Jeśli dzielisz łańcuch w Lua, powinieneś wypróbować metody string.gmatch () lub string.sub (). Użyj metody string.sub (), jeśli znasz indeks, w którym chcesz podzielić ciąg, lub użyj string.gmatch (), jeśli przeanalizujesz ciąg w celu znalezienia lokalizacji, w której chcesz podzielić ciąg.

Przykład użycia string.gmatch () z Lua 5.1 Reference Manual :

 t = {}
 s = "from=world, to=Lua"
 for k, v in string.gmatch(s, "(%w+)=(%w+)") do
   t[k] = v
 end
gwell
źródło
"Pożyczyłem" implementację z tej strony lua-users i tak dziękuję
RCIX
24

Jeśli chcesz tylko iterować po tokenach, jest to całkiem fajne:

line = "one, two and 3!"

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

Wynik:

jeden,

dwa

i

3!

Krótkie wyjaśnienie: wzorzec „[^% s] +” pasuje do każdego niepustego ciągu między znakami spacji.

Hugo
źródło
2
Wzór %Sjest równy temu, o którym wspomniałeś, podobnie jak %Snegacja %s, podobnie jak %Dnegacja %d. Dodatkowo %wjest równe [A-Za-z0-9_](inne znaki mogą być obsługiwane w zależności od ustawień regionalnych).
Lars Gyrup Brink Nielsen
14

Podobnie jak w string.gmatchprzypadku wyszukiwania wzorców w ciągu, ta funkcja znajdzie rzeczy między wzorami:

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

Domyślnie zwraca wszystko, co jest oddzielone białymi znakami.

Norman Ramsey
źródło
6
+1. Uwaga dla innych początkujących użytkowników Lua: zwraca to iterator, a „między wzorcami” obejmuje początek i koniec łańcucha. (Jako nowicjusz musiałem spróbować to rozgryźć.)
Darius Bacon,
12

Oto funkcja:

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

Nazwij to tak:

list=split(string_to_split,pattern_to_match)

na przykład:

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


Więcej informacji można znaleźć tutaj:
http://lua-users.org/wiki/SplitJoin

Faisal Hanif
źródło
7

Podoba mi się to krótkie rozwiązanie

function split(s, delimiter)
    result = {};
    for match in (s..delimiter):gmatch("(.-)"..delimiter) do
        table.insert(result, match);
    end
    return result;
end
Ivo Beckers
źródło
To mój ulubiony, ponieważ jest taki krótki i prosty. Nie bardzo rozumiem, co się dzieje, czy ktoś mógłby mi wyjaśnić?
sześciokąt
2
To kończy się niepowodzeniem, gdy używasz kropki jako separatora (lub potencjalnie dowolnego innego magicznego znaku wzoru)
TurboHz
6

Ponieważ istnieje więcej niż jeden sposób oskórowania kota, oto moje podejście:

Kod :

#!/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

Wyjście : 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.

Wyjaśnienie :

Te gmatchprace funkcjonowała jako iterator, to pobiera wszystkie sznurki pasujących regex. regexWykonuje wszystkie znaki aż znajdzie separator.

Diego Pino
źródło
5

Możesz użyć tej metody:

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
źródło
5

Wiele z tych odpowiedzi akceptuje tylko separatory jednoznakowe lub nie radzi sobie dobrze z przypadkami brzegowymi (np. Puste separatory), więc pomyślałem, że zapewnię bardziej ostateczne rozwiązanie.

Oto dwie funkcje gspliti split, zaadaptowane z kodu w rozszerzeniu Scribunto MediaWiki , które jest używane na stronach wiki, takich jak Wikipedia. Kod jest objęty licencją GPL v2 . Zmieniłem nazwy zmiennych i dodałem komentarze, aby kod był nieco łatwiejszy do zrozumienia, a także zmieniłem kod tak, aby używał zwykłych wzorców ciągów Lua zamiast wzorców Scribunto dla ciągów Unicode. Oryginalny kod zawiera tutaj przypadki testowe .

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

Kilka przykładów splitużywanej funkcji:

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
źródło
5

sposób, jakiego nie ma u innych

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
źródło
4

Po prostu siedząc na ograniczniku

local str = 'one,two'
local regxEverythingExceptComma = '([^,]+)'
for x in string.gmatch(str, regxEverythingExceptComma) do
    print(x)
end
Jerome Anthony
źródło
3

Wykorzystałem powyższe przykłady do stworzenia własnej funkcji. Ale brakującym dla mnie elementem automatycznie wymykał się magicznym postaciom.

Oto mój wkład:

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
nieustraszony bohater
źródło
To też był mój duży problem. Działa to świetnie z magicznymi postaciami, ładnymi
Andrew White
1

Możesz użyć biblioteki Penlight . Ma funkcję dzielenia łańcucha za pomocą separatora, który wyświetla listę.

Zaimplementował wiele funkcji, których możemy potrzebować podczas programowania i których brakuje w Lua.

Oto przykład użycia go.

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

źródło
0

W zależności od przypadku użycia może to być przydatne. Wycina cały tekst po obu stronach flag:

b = "This is a string used for testing"

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

print (c)

Wynik:

string
zieleń
źródło
0

Bardzo późno na to pytanie, ale na wypadek, gdyby ktoś chciał mieć wersję obsługującą liczbę podziałów, które chcesz uzyskać .....

-- 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
źródło
0

Jeśli programujesz w Lua, nie masz tu szczęścia. Lua jest JEDYNYM językiem programowania, który tak się składa, że ​​jest notorycznie niesławny, ponieważ jego autorzy nigdy nie zaimplementowali funkcji podziału w bibliotece standardowej, a zamiast tego napisali 16 ekranów pełnych wyjaśnień i kiepskich wymówek, dlaczego tego nie zrobili, a czego nie, przeplatane licznymi półpracującymi przykładami, które praktycznie gwarantują pracę dla prawie wszystkich, ale nie sprawdzają się w Twojej narożnej sprawie. To jest po prostu najnowocześniejszy Lua, a wszyscy, którzy programują w Lua, po prostu zaciskają zęby i powtarzają postacie. Istnieje wiele rozwiązań, które czasami są lepsze, ale dokładnie zero rozwiązań, które są niezawodnie lepsze.

Szczepan Hołyszewski
źródło