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


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

.


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

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


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.


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


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

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.


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) 

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

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

4

Po prostu siedząc na ograniczniku

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

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

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

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

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

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.