Разделить строку в Lua?
Мне нужно сделать простое разбиение строки, но, похоже, для этого не существует функции, а ручной метод, который я тестировал, не сработал. Как бы я это сделал?
21 ответ
Пожалуйста, смотрите разделение строк:
Вот различные способы разбить строку на список подстрок, разбить исходную строку при появлении некоторого разделителя (символа, набора символов или шаблона). Обычно это называется функцией разделения строк [2].
Вот мое действительно простое решение. Используйте функцию gmatch для захвата строк, которые содержат как минимум ОДИН символ НИЧЕГО, кроме требуемого разделителя. Разделителем является ЛЮБОЙ пробел (%s в Lua) по умолчанию:
function mysplit(inputstr, sep)
if sep == nil then
sep = "%s"
end
local t={} ; i=1
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
t[i] = str
i = i + 1
end
return t
end
Если вы разбиваете строку в Lua, вам следует попробовать методы string.gmatch () или string.sub(). Используйте метод string.sub(), если вам известен индекс, по которому вы хотите разделить строку, или используйте string.gmatch (), если вы проанализируете строку, чтобы найти место для разделения строки.
Пример использования string.gmatch () из справочного руководства Lua 5.1:
t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
t[k] = v
end
Если вы просто хотите перебрать токены, это довольно аккуратно:
line = "one, two and 3!"
for token in string.gmatch(line, "[^%s]+") do
print(token)
end
Выход:
один,
два
а также
3!
Краткое объяснение: шаблон "[^%s]+" соответствует каждой непустой строке между пробелами.
Как только string.gmatch
найдет шаблоны в строке, эта функция найдет вещи между шаблонами:
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
По умолчанию он возвращает все, что разделено пробелом.
Если вы программируете на Lua, вам здесь не повезло. Lua - это тот язык программирования, который стал печально известным, потому что его авторы никогда не реализовывали "функцию разделения" в стандартной библиотеке, а вместо этого написали 16 экранов с объяснениями и неубедительными отговорками, почему они этого не сделали и не будут. перемежается многочисленными полуработающими примерами, которые практически гарантированно работают почти для всех, но не подходят для вашего углового случая. Это просто современный язык Lua, и каждый, кто программирует на Lua, просто стискивает зубы и перебирает символы. Существует множество решений, которые иногда лучше, но ровно ноль решений, которые надежно лучше.
Вот функция:
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
Назовите это как:
list=split(string_to_split,pattern_to_match)
например:
list=split("1:2:3:4","\:")
Для получения дополнительной перейдите сюда:
http://lua-users.org/wiki/SplitJoin
Поскольку существует более одного способа снятия шкуры с кошки, вот мой подход:
Код:
#!/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
Выход:
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.
Пояснение:
gmatch
функция работает как итератор, она выбирает все подходящие строки regex
, regex
принимает все символы, пока не найдет разделитель.
Многие из этих ответов принимают только односимвольные разделители или плохо разбираются в крайних случаях (например, пустые разделители), поэтому я подумал, что предоставлю более определенное решение.
Вот две функции, gsplit
а также split
, адаптированный из кода в расширении Scribunto MediaWiki, которое используется в вики, таких как Википедия. Код распространяется по лицензии GPL v2. Я изменил имена переменных и добавил комментарии, чтобы немного облегчить понимание кода, и я также изменил код, чтобы использовать обычные строковые шаблоны Lua вместо шаблонов Scribunto для строк Unicode. Оригинальный код имеет тестовые случаи здесь.
-- 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
Некоторые примеры split
используемая функция:
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
Мне нравится это короткое решение
function split(s, delimiter)
result = {};
for match in (s..delimiter):gmatch("(.-)"..delimiter) do
table.insert(result, match);
end
return result;
end
Вы можете использовать этот метод:
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)
Путь не виден в других
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
Вы можете использовать библиотеку фонарика. У этого есть функция для разделения строки с помощью разделителя, который выводит список.
В нем реализованы многие функции, которые могут понадобиться нам при программировании и которые отсутствуют в Lua.
Вот пример его использования.
>
> stringx = require "pl.stringx"
>
> str = "welcome to the world of lua"
>
> arr = stringx.split(str, " ")
>
> arr
{welcome,to,the,world,of,lua}
>
Просто сидим на разделителе
local str = 'one,two'
local regxEverythingExceptComma = '([^,]+)'
for x in string.gmatch(str, regxEverythingExceptComma) do
print(x)
end
Очень поздно ответить на этот вопрос, но в случае, если кому-то нужна версия, которая обрабатывает количество разделений, которое вы хотите получить...
-- 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
Я использовал приведенные выше примеры для создания своей собственной функции. Но пропавшая часть для меня автоматически убегала от магических персонажей.
Вот мой вклад:
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
В зависимости от варианта использования это может быть полезно. Он обрезает весь текст по обе стороны от флагов:
b = "This is a string used for testing"
--Removes unwanted text
c = (b:match("a([^/]+)used"))
print (c)
Выход:
string
Для тех, кто пришел к упражнению 10.1 книги "Программирование на Lua", кажется очевидным, что мы не можем использовать понятие, описанное далее в книге (итератор), и что функция должна принимать более одного разделителя символов.
The split()
это трюк, чтобы получить шаблон, соответствующий тому, что не нужно (разделение), и вернуть пустую таблицу в пустой строке. ВозвращениеplainSplit()
больше похоже на раскол на другом языке.
magic = "([%%%.%(%)%+%*%?%[%]%^%$])"
function split(str, sep, plain)
if plain then sep = string.gsub(sep, magic, "%%%1") end
local N = '\255'
str = N..str..N
str = string.gsub(str, sep, N..N)
local result = {}
for word in string.gmatch(str, N.."(.-)"..N) do
if word ~= "" then
table.insert(result, word)
end
end
return result
end
function plainSplit(str, sep)
sep = string.gsub(sep, magic, "%%%1")
local result = {}
local start = 0
repeat
start = start + 1
local from, to = string.find(str, sep, start)
from = from and from-1
local word = string.sub(str, start, from, true)
table.insert(result, word)
start = to
until start == nil
return result
end
function tableToString(t)
local ret = "{"
for _, word in ipairs(t) do
ret = ret .. '"' .. word .. '", '
end
ret = string.sub(ret, 1, -3)
ret = ret .. "}"
return #ret > 1 and ret or "{}"
end
function runSplit(func, title, str, sep, plain)
print("\n" .. title)
print("str: '"..str.."'")
print("sep: '"..sep.."'")
local t = func(str, sep, plain)
print("-- t = " .. tableToString(t))
end
print("\n\n\n=== Pattern split ===")
runSplit(split, "Exercice 10.1", "a whole new world", " ")
runSplit(split, "With trailing seperator", " a whole new world ", " ")
runSplit(split, "A word seperator", "a whole new world", " whole ")
runSplit(split, "Pattern seperator", "a1whole2new3world", "%d")
runSplit(split, "Magic characters as plain seperator", "a$.%whole$.%new$.%world", "$.%", true)
runSplit(split, "Control seperator", "a\0whole\1new\2world", "%c")
runSplit(split, "ISO Time", "2020-07-10T15:00:00.000", "[T:%-%.]")
runSplit(split, " === [Fails] with \\255 ===", "a\255whole\0new\0world", "\0", true)
runSplit(split, "How does your function handle empty string?", "", " ")
print("\n\n\n=== Plain split ===")
runSplit(plainSplit, "Exercice 10.1", "a whole new world", " ")
runSplit(plainSplit, "With trailing seperator", " a whole new world ", " ")
runSplit(plainSplit, "A word seperator", "a whole new world", " whole ")
runSplit(plainSplit, "Magic characters as plain seperator", "a$.%whole$.%new$.%world", "$.%")
runSplit(plainSplit, "How does your function handle empty string?", "", " ")
выход
=== Pattern split ===
Exercice 10.1
str: 'a whole new world'
sep: ' '
-- t = {"a", "whole", "new", "world"}
With trailing seperator
str: ' a whole new world '
sep: ' '
-- t = {"a", "whole", "new", "world"}
A word seperator
str: 'a whole new world'
sep: ' whole '
-- t = {"a", "new world"}
Pattern seperator
str: 'a1whole2new3world'
sep: '%d'
-- t = {"a", "whole", "new", "world"}
Magic characters as plain seperator
str: 'a$.%whole$.%new$.%world'
sep: '$.%'
-- t = {"a", "whole", "new", "world"}
Control seperator
str: 'awholenewworld'
sep: '%c'
-- t = {"a", "whole", "new", "world"}
ISO Time
str: '2020-07-10T15:00:00.000'
sep: '[T:%-%.]'
-- t = {"2020", "07", "10", "15", "00", "00", "000"}
=== [Fails] with \255 ===
str: 'a�wholenewworld'
sep: ''
-- t = {"a"}
How does your function handle empty string?
str: ''
sep: ' '
-- t = {}
=== Plain split ===
Exercice 10.1
str: 'a whole new world'
sep: ' '
-- t = {"a", "whole", "new", "world"}
With trailing seperator
str: ' a whole new world '
sep: ' '
-- t = {"", "", "a", "", "whole", "", "", "new", "world", "", ""}
A word seperator
str: 'a whole new world'
sep: ' whole '
-- t = {"a", "new world"}
Magic characters as plain seperator
str: 'a$.%whole$.%new$.%world'
sep: '$.%'
-- t = {"a", "whole", "new", "world"}
How does your function handle empty string?
str: ''
sep: ' '
-- t = {""}
Есть пример(unexpandTabs
) в конце раздела «Замены» книги «Программирование на Lua», 4-е изд., Глава 10, в которой используется SOH
символ (), чтобы пометить столбцы табуляции для последующей обработки. Я подумал, что это отличная идея, поэтому адаптировал ее к идеям «соответствовать всему, кроме символа-разделителя», которые используются во многих ответах здесь. Предварительно обрабатывая входную строку для замены всех совпадений на , мы можем поддерживать произвольные шаблоны разделителей , что делают только некоторые ответы, например отличный ответ @norman-ramsey .
Я также включилexclude_empty
параметр с поведением по умолчанию просто для развлечения.
Очевидно, что это приведет к неправильному выводу, если входная строка содержит\1
, но это кажется крайне маловероятным в любом случае за пределами обмена специализированными протоколами.
function string:split(pat, exclude_empty)
pat = pat or "%s+"
self = self:gsub(pat, "\1")
local res = {}
for match in self:gmatch("([^\1]" .. (exclude_empty and "+" or "*") .. ")") do
res[#res + 1] = match
end
return res
end
Я обнаружил, что многие другие ответы имели крайние случаи, которые не удались (например, когда данная строка содержит#
,{
или}
символы, или когда задан символ-разделитель, например%
которые требуют побега). Вот реализация, с которой я пошел вместо этого:
local function newsplit(delimiter, str)
assert(type(delimiter) == "string")
assert(#delimiter > 0, "Must provide non empty delimiter")
-- Add escape characters if delimiter requires it
delimiter = delimiter:gsub("[%(%)%.%%%+%-%*%?%[%]%^%$]", "%%%0")
local start_index = 1
local result = {}
while true do
local delimiter_index, _ = str:find(delimiter, start_index)
if delimiter_index == nil then
table.insert(result, str:sub(start_index))
break
end
table.insert(result, str:sub(start_index, delimiter_index - 1))
start_index = delimiter_index + 1
end
return result
end
Вот процедура, которая работает в Lua 4.0, возвращая таблицу t подстрок в inputstr , разделенных sep :
function string_split(inputstr, sep)
local inputstr = inputstr .. sep
local idx, inc, t = 0, 1, {}
local idx_prev, substr
repeat
idx_prev = idx
inputstr = strsub(inputstr, idx + 1, -1) -- chop off the beginning of the string containing the match last found by strfind (or initially, nothing); keep the rest (or initially, all)
idx = strfind(inputstr, sep) -- find the 0-based r_index of the first occurrence of separator
if idx == nil then break end -- quit if nothing's found
substr = strsub(inputstr, 0, idx) -- extract the substring occurring before the separator (i.e., data field before the next delimiter)
substr = gsub(substr, "[%c" .. sep .. " ]", "") -- eliminate control characters, separator and spaces
t[inc] = substr -- store the substring (i.e., data field)
inc = inc + 1 -- iterate to next
until idx == nil
return t
end
Этот простой тест
inputstr = "the brown lazy fox jumped over the fat grey hen ... or something."
sep = " "
t = {}
t = string_split(inputstr,sep)
for i=1,15 do
print(i, t[i])
end
Урожайность:
--> t[1]=the
--> t[2]=brown
--> t[3]=lazy
--> t[4]=fox
--> t[5]=jumped
--> t[6]=over
--> t[7]=the
--> t[8]=fat
--> t[9]=grey
--> t[10]=hen
--> t[11]=...
--> t[12]=or
--> t[13]=something.