Работа со строками, таблицами и файлами
Работа со строками, таблицами и файлами
Что такое строка и как она устроена
Строка — это последовательность символов, используемая для представления текстовых данных. В языке программирования Lua строка является примитивным типом данных, который не изменяется после создания. Это свойство называется неизменяемостью (immutability). Любая операция, которая должна изменить содержимое строки, фактически создает новую строку с измененными данными, оставляя исходную строку без изменений.
Внутреннее представление строки в Lua представляет собой массив байтов. Каждый символ в строке занимает один байт. Для стандартного ASCII-диапазона это соответствует одному символу на один байт. При работе с Unicode-символами (например, кириллицей, эмодзи или иероглифами) ситуация усложняется: один символ может занимать от одного до четырех байт в зависимости от кодировки. Lua по умолчанию использует UTF-8 для строк, что позволяет корректно обрабатывать символы практически всех письменностей мира.
Система управления памятью Lua автоматически отслеживает количество ссылок на каждую созданную строку. Если несколько переменных ссылаются на одну и ту же строковую литерал, система памяти объединяет их в единый объект. Это явление называется интернированием строк (string interning). Интернирование экономит память и ускоряет сравнение строк, так как достаточно проверить равенство указателей на объекты, а не перебирать все символы.
local str1 = "Привет"
local str2 = "Привет"
print(str1 == str2) -- true
-- str1 и str2 указывают на один и тот же объект в памяти
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Строки в Lua могут быть заданы двумя способами: с помощью одинарных кавычек '...' или двойных кавычек "...". Оба способа полностью эквивалентны. Разница заключается лишь в удобстве использования при наличии внутри строки соответствующих кавычек. Если строка содержит двойные кавычки, её удобно заключить в одинарные, и наоборот.
Для вставки специальных символов в строку используется механизм экранирования. Символ обратной косой черты \ служит сигналом того, что следующий за ним символ имеет особое значение. Стандартные управляющие последовательности включают:
\n— перевод строки;\r— возврат каретки;\t— горизонтальная табуляция;\\— обратная косая черта;\"— двойная кавычка;\'— одинарная кавычка.
Lua также поддерживает шестнадцатеричное представление символов через последовательность \xHH, где HH — два шестнадцатеричных символа. Это позволяет точно задать любой байт в диапазоне от 0 до 255.
local message = "Строка с переносом\nИ вторая строка."
local tab = "Текст\tс табуляцией"
local quote = "Он сказал: \"Привет\""
local hex = "\x41\x42\x43" -- ABC
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Для работы с многострочными текстами, содержащими кавычки и сложные символы, в Lua существует специальный синтаксис с использованием длинных скобок [=[...]]=. Скобки могут иметь разную длину, но открывающая и закрывающая конструкции должны совпадать по количеству квадратных скобок. Внутри такого блока любые символы, включая кавычки и обратные слеши, воспринимаются буквально.
local multiline = [[
Это многострочная строка.
Она содержит кавычки "и '".
Здесь нет необходимости экранировать \ символы.
]]
Разбор:
- Одинарные и двойные кавычки эквивалентны: выбирайте тот вид, где меньше экранирования внутри текста.
- При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Длина строки определяется количеством символов в ней. Функция # возвращает длину строки. В случае со строками, содержащими многобайтовые символы, оператор # возвращает количество байт, а не количество символов. Для получения точного количества символов необходимо использовать функции из модуля utf8.
local text = "Привет"
print(#text) -- выведет 6, так как каждый кириллический символ занимает 2 байта
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Оператор
#для строк и последовательных таблиц возвращает длину (для строк — в байтах, не всегда в символах UTF-8). - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Основные операции над строками
Конкатенация
Конкатенация — это процесс соединения двух или более строк в одну. В Lua для этой цели используется оператор .. (две точки). Оператор соединяет строки слева направо.
local first = "Hello"
local second = "World"
local result = first .. " " .. second
print(result) -- Hello World
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
При конкатенации участвуют не только строковые типы, но и другие типы данных. Lua автоматически выполняет приведение типов: числа, булевы значения и nil преобразуются в строковое представление. Число превращается в его десятичную запись, булево значение — в строку "true" или "false", а nil — в пустую строку.
local number = 42
local boolean = true
local nilValue = nil
print("Число: " .. number) -- Число: 42
print("Булево: " .. boolean) -- Булево: true
print("Nil: " .. nilValue) -- Nil:
Разбор:
- Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Сравнение строк
Сравнение строк в Lua осуществляется операторами == (равно) и ~= (не равно). Сравнение происходит побайтово. Для латинского алфавита порядок символов соответствует их кодам в таблице ASCII. Для других алфавитов порядок определяется в соответствии с таблицей Unicode.
local a = "abc"
local b = "abd"
print(a < b) -- true, так как 'c' меньше 'd'
print(a == "abc") -- true
print(a ~= "xyz") -- true
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Одинарные и двойные кавычки эквивалентны: выбирайте тот вид, где меньше экранирования внутри текста.
- Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Преобразование типов
Функция tostring() преобразует любое значение в строковый формат. Для чисел она возвращает их десятичное представление. Для таблиц, функций и кортежей она возвращает строку вида "table: 0x...", где указатель на адрес памяти.
print(tostring(123)) -- "123"
print(tostring(true)) -- "true"
print(tostring({1, 2})) -- "table: 0x..."
Разбор:
- Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). - Ключевые вызовы в фрагменте:
print(),tostring(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция tonumber() выполняет обратное преобразование: строка в число. Если строка не содержит валидного числового представления, функция возвращает nil.
print(tonumber("42")) -- 42
print(tonumber("abc")) -- nil
Разбор:
- Ключевые вызовы в фрагменте:
print(),tonumber(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Поиск подстрок
Функция string.find(s, pattern) ищет первое вхождение шаблона в строке. Она возвращает индекс начала найденной подстроки и индекс её конца. Если подстрока не найдена, функция возвращает nil.
local text = "Lua Programming"
local start, finish = string.find(text, "Programming")
print(start, finish) -- 5 14
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.find(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Аргумент pattern может быть простой строкой или регулярным выражением. Если передан просто текст, поиск выполняется как точное совпадение.
local text = "Hello World"
print(string.find(text, "World")) -- 7 11
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.find(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция string.sub(s, i, j) извлекает подстроку из строки s, начиная с индекса i и заканчивая индексом j. Индексация начинается с единицы. Если второй индекс опущен, подстрока берется до конца строки. Отрицательные индексы считаются с конца строки.
local text = "Lua"
print(string.sub(text, 1, 2)) -- Lu
print(string.sub(text, -1)) -- a
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.sub(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Методы работы со строками в Lua
Форматирование строк
Функция string.format(fmt, ...) формирует строку на основе формата и списка аргументов. Формат строки начинается со знака % и указывает тип данных и способ вывода.
Основные спецификаторы формата:
%s— строка;%d— целое число;%f— число с плавающей точкой;%x— число в шестнадцатеричном формате;%%— сам знак процента.
local name = "Alice"
local age = 30
local greeting = string.format("Привет, %s! Тебе %d лет.", name, age)
print(greeting) -- Привет, Alice! Тебе 30 лет.
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.format(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Можно задавать ширину поля и точность вывода. Например, %5d выводит число в поле шириной 5 символов, дополняя пробелами слева. %5.2f выводит число с плавающей точкой с шириной 5 и точностью 2 знака после запятой.
print(string.format("%5d", 42)) -- " 42"
print(string.format("%.2f", 3.14159)) -- "3.14"
Разбор:
- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.format(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Разбиение строк
Функция string.split отсутствует в стандартной библиотеке Lua, поэтому разбиение строк реализуется через циклы и функцию string.gmatch или string.match.
Функция string.gmatch(s, pattern) возвращает итератор, который проходит по всем вхождениям шаблона в строке. Это позволяет легко извлекать слова, разделенные пробелами или другими разделителями.
local text = "один два три"
for word in string.gmatch(text, "%S+") do
print(word)
end
-- Вывод:
-- один
-- два
-- три
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.gmatch(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Для разбиения строки по конкретному разделителю можно использовать цикл с функцией string.find.
Код ITЗагрузка примера кода…
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. whileпроверяет условие до входа в тело;repeat…until— хотя бы один проход, выход при истинномuntil.if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.- Вызовы из стандартных библиотек
string,table— готовые функции языка, не нужно писать их с нуля.
Замена подстрок
Функция string.gsub(s, pattern, repl) заменяет все вхождения шаблона в строке на указанную замену. Возвращает новую строку и количество выполненных замен.
local text = "cat dog cat"
local new_text, count = string.gsub(text, "cat", "dog")
print(new_text) -- dog dog dog
print(count) -- 2
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.gsub(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
В качестве замены можно использовать строку, функцию или таблицу. Если передана функция, она вызывается для каждого найденного совпадения с аргументами, равными найденным подстрокам.
local text = "one two three"
local upper_text = string.gsub(text, "%w+", function(word)
return string.upper(word)
end)
print(upper_text) -- ONE TWO THREE
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.returnзавершает функцию и может вернуть несколько значений через запятую.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.gsub(),string.upper(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Приведение регистра
Функции string.upper(s) и string.lower(s) преобразуют строку соответственно в верхний и нижний регистр. Эти функции учитывают национальные особенности алфавита.
print(string.upper("hello")) -- HELLO
print(string.lower("HELLO")) -- hello
Разбор:
- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.lower(),string.upper(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Удаление пробелов
Функции string.trim, string.ltrim и string.rtrim отсутствуют в стандартной библиотеке Lua. Их реализацию можно выполнить вручную или использовать регулярные выражения.
local text = " hello world "
local trimmed = string.gsub(text, "^%s*(.-)%s*$", "%1")
print(trimmed) -- hello world
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.gsub(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция string.gsub с паттерном ^%s* удаляет пробелы в начале строки, а (.-)%s*$ — в конце.
Проверка содержимого
Функции string.match(s, pattern) и string.find(s, pattern) позволяют проверять наличие определенных конструкций в строке. string.match возвращает первое найденное совпадение или nil.
local email = "user@example.com"
if string.match(email, "@") then
print("Строка содержит @")
end
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.match(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Проверка на цифру:
if string.match("123", "^%d+$") then
print("Строка состоит только из цифр")
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),string.match(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Что такое таблица и как она устроена
Таблица — это универсальная структура данных в Lua, представляющая собой ассоциативный массив. Таблица связывает ключи со значениями. Ключом может служить любой тип данных, кроме nil и NaN. Значением может быть любой тип данных, включая другую таблицу, функцию или ссылку на себя.
Таблица в Lua — это динамическая структура. Её размер меняется автоматически при добавлении или удалении элементов. Нет необходимости заранее выделять память или указывать максимальное количество элементов.
Внутреннее устройство таблицы в Lua оптимизировано для различных случаев использования. Lua использует гибридную структуру хранения: массивная часть для целочисленных ключей и хеш-часть для остальных ключей. Это обеспечивает быстрый доступ к элементам как по числовым, так и по строковым индексам.
local t = {10, 20, 30}
print(t[1]) -- 10
print(t[2]) -- 20
print(t[3]) -- 30
Разбор:
- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Ключи в таблице могут быть любого типа. Строковые ключи часто используются для создания объектов или структур данных.
local person = {
name = "Ivan",
age = 25,
city = "Moscow"
}
print(person.name) -- Ivan
Разбор:
- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Таблицы поддерживают рекурсивную структуру. Таблица может содержать другие таблицы, создавая древовидные структуры данных.
local company = {
name = "TechCorp",
departments = {
sales = { head = "Anna" },
it = { head = "Petr" }
}
}
print(company.departments.it.head) -- Petr
Разбор:
- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Основные операции с таблицами
Создание таблицы
Таблицу можно создать с помощью фигурных скобок {}. Элементы разделяются запятыми. Если элементы имеют имена (ключи), они записываются в виде key = value. Если ключи не указаны, они присваиваются автоматически, начиная с единицы.
local empty_table = {}
local numbers = {1, 2, 3, 4, 5}
local mixed = {name = "Test", value = 100, active = true}
Разбор:
- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Добавление и удаление элементов
Элементы добавляются в таблицу путем присваивания значения по ключу.
local t = {}
t[1] = "first"
t["second"] = 2
t[3] = {nested = true}
Разбор:
- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Удаление элемента осуществляется путем присваивания ему значения nil.
t["second"] = nil
Разбор:
- Код выполняется построчно: каждая инструкция использует значения, созданные строками выше.
- При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Перебор таблицы
Для перебора элементов таблицы используются итераторы. Наиболее распространенный итератор — pairs(). Он проходит по всем парам ключ-значение в произвольном порядке.
local t = {a = 1, b = 2, c = 3}
for key, value in pairs(t) do
print(key, value)
end
Разбор:
ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
pairs(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Для перебора только числовых ключей, расположенных последовательно, используется итератор ipairs(). Он останавливается при встрече первого nil.
local t = {10, 20, 30, nil, 40}
for index, value in ipairs(t) do
print(index, value)
end
-- Вывод:
-- 1 10
-- 2 20
-- 3 30
-- Цикл завершается, так как элемент 4 равен nil
Разбор:
ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
ipairs(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Изменение размера таблицы
Функция table.insert(t, [pos], value) вставляет элемент в таблицу. Если указан индекс pos, элемент вставляется перед существующим элементом. Если индекс не указан, элемент добавляется в конец.
local t = {1, 2}
table.insert(t, 3)
print(t[3]) -- 3
table.insert(t, 2, 10)
print(t[2]) -- 10
print(t[3]) -- 2
Разбор:
- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),table.insert(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция table.remove(t, [pos]) удаляет элемент из таблицы. По умолчанию удаляется последний элемент.
local t = {1, 2, 3}
table.remove(t)
print(t[2]) -- 2
table.remove(t, 1)
print(t[1]) -- 2
Разбор:
- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),table.remove(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Сортировка таблицы
Функция table.sort(t, comp) сортирует элементы таблицы. Аргумент comp — функция сравнения, которая принимает два элемента и возвращает true, если первый должен идти перед вторым.
local numbers = {5, 2, 8, 1, 9}
table.sort(numbers)
print(table.concat(numbers, ", ")) -- 1, 2, 5, 8, 9
Разбор:
- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),table.concat(),table.sort(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Для сортировки таблиц по определенным полям используется пользовательская функция сравнения.
local people = {
{name = "Anna", age = 30},
{name = "Boris", age = 25},
{name = "Cyril", age = 35}
}
table.sort(people, function(a, b)
return a.age < b.age
end)
for _, p in ipairs(people) do
print(p.name, p.age)
end
Разбор:
ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. returnзавершает функцию и может вернуть несколько значений через запятую.- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
ipairs(),print(),table.sort(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Объединение таблиц
Функция table.concat(t, sep, i, j) объединяет элементы таблицы в строку, используя разделитель sep. Диапазон элементов указывается параметрами i и j.
local words = {"Hello", "world"}
print(table.concat(words, " ")) -- Hello world
Разбор:
- Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
print(),table.concat(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Для объединения двух таблиц можно использовать цикл и оператор присваивания.
local t1 = {1, 2}
local t2 = {3, 4}
for _, v in ipairs(t2) do
table.insert(t1, v)
end
print(table.concat(t1, ", ")) -- 1, 2, 3, 4
Разбор:
ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте —
ipairs(),print(),table.concat(),table.insert(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Поиск элементов
Функция table.find(t, value) отсутствует в стандартной библиотеке. Поиск выполняется вручную с помощью цикла.
function find(t, value)
for k, v in pairs(t) do
if v == value then
return k
end
end
return nil
end
local t = {a = 10, b = 20}
print(find(t, 20)) -- b
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.returnзавершает функцию и может вернуть несколько значений через запятую.- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
pairs(),print(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Что такое файл и как он устроен
Файл — это именованный набор данных, хранящийся на носителе информации. В Lua работа с файлами осуществляется через встроенную библиотеку io. Библиотека предоставляет функции для открытия, чтения, записи и закрытия файлов.
Файл в Lua представляется объектом типа file. Объект файла содержит информацию о текущем положении курсора, режиме доступа и пути к файлу. Все операции с файлом выполняются через методы этого объекта или глобальные функции.
Режимы доступа к файлам:
"r"— чтение (файл должен существовать);"w"— запись (файл создается или очищается);"a"— добавление (файл создается или данные дописываются в конец);"r+"— чтение и запись (файл должен существовать);"w+"— чтение и запись (файл создается или очищается);"a+"— чтение и добавление (файл создается или данные дописываются).
local file = io.open("test.txt", "r")
if file then
print(file:read())
file:close()
else
print("Файл не открыт")
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Открытие файла
Функция io.open(filename, mode) открывает файл и возвращает объект файла или nil в случае ошибки. Второй аргумент mode указывает режим доступа.
local file = io.open("data.txt", "w")
if file then
file:write("Привет, мир!")
file:close()
else
print("Ошибка открытия файла")
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция io.input(filename) устанавливает текущий входной файл по умолчанию. Все последующие вызовы io.read() будут читать из этого файла.
io.input("input.txt")
local line = io.read()
Разбор:
- Ключевые вызовы в фрагменте:
io.input(),io.read(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция io.output(filename) устанавливает текущий выходной файл по умолчанию. Все последующие вызовы io.write() будут писать в этот файл.
io.output("output.txt")
io.write("Текст для записи")
Разбор:
- Ключевые вызовы в фрагменте:
io.output(),io.write(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Чтение из файла
Метод file:read([size]) читает данные из файла. Аргумент size определяет формат чтения:
number— прочитатьnбайт;"line"— прочитать одну строку (до перевода строки);"lines"— вернуть итератор для чтения всех строк;nil— прочитать следующую строку.
local file = io.open("text.txt", "r")
if file then
local line = file:read()
print(line)
file:close()
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Для чтения всего содержимого файла можно использовать итератор.
local file = io.open("text.txt", "r")
if file then
for line in file:lines() do
print(line)
end
file:close()
end
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Запись в файл
Метод file:write(...) записывает данные в файл. Аргументы могут быть строками или числами.
local file = io.open("log.txt", "a")
if file then
file:write("Запись №1\n")
file:write("Запись №2\n")
file:close()
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Позиционирование в файле
Метод file:seek([whence][, offset]) перемещает курсор чтения/записи в файле. Аргумент whence указывает точку отсчета:
"set"— начало файла (по умолчанию);"cur"— текущая позиция;"end"— конец файла.
Аргумент offset — смещение в байтах.
local file = io.open("data.bin", "r+b")
if file then
file:seek("set", 0) -- Начало файла
local content = file:read("*all")
file:seek("cur", 10) -- Смещение на 10 байт вперед
local part = file:read(5)
file:seek("end", -5) -- 5 байт от конца
local tail = file:read("*all")
file:close()
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Закрытие файла
Метод file:close() закрывает файл и освобождает ресурсы. После закрытия файл нельзя использовать для чтения или записи.
local file = io.open("test.txt", "w")
file:write("Текст")
file:close()
Разбор:
io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Обработка ошибок
Все операции с файлами могут завершиться ошибкой. Рекомендуется проверять результат вызова io.open. Если возвращается nil, следует обработать ошибку.
local file, err = io.open("nonexistent.txt", "r")
if not file then
print("Ошибка:", err)
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Ключевые вызовы в фрагменте:
io.open(),print(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Пример 1 — Парсинг CSV-файла
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string,table— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
io.open(),ipairs(),print(),string.gmatch().
Пример 2 — Сохранение и загрузка конфигурации
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.ipairsобходит последовательную часть таблицы (индексы 1,2,3…);pairs— все пары ключ-значение.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1.
Пример 3 — Обработка больших файлов
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
io.open(),string.gsub(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Пример 4 — Работа с бинарными файлами
Код ITЗагрузка примера кода…
Разбор:
functionобъявляет функцию;endзакрывает тело. Имя послеfunctionстановится ссылкой на вызываемый объект.if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
io.open(),print(),string.char(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Поддержка UTF-8 в Lua
Lua 5.3 и выше имеет встроенную поддержку UTF-8 через модуль utf8. Модуль предоставляет функции для работы с кодировкой Unicode.
Функция utf8.len(s) возвращает количество символов в строке, учитывая многобайтовую кодировку.
local text = "Привет"
print(utf8.len(text)) -- 6
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
utf8— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),utf8.len(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция utf8.codes(s) возвращает итератор, выдающий коды символов в строке.
for code in utf8.codes("A") do
print(code) -- 65
end
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Вызовы из стандартных библиотек
utf8— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),utf8.codes(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция utf8.char(...) создает строку из кодов символов.
print(utf8.char(65, 66, 67)) -- ABC
Разбор:
- Вызовы из стандартных библиотек
utf8— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),utf8.char(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Функция utf8.offset(s, n) возвращает позицию байта, соответствующего n-му символу в строке.
local text = "Привет"
print(utf8.offset(text, 1)) -- 1 (первый символ)
print(utf8.offset(text, 2)) -- 3 (второй символ)
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Вызовы из стандартных библиотек
utf8— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
print(),utf8.offset(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Обработка ошибок кодировки
При чтении файлов с неправильной кодировкой могут возникать ошибки. Рекомендуется явно указывать кодировку при чтении и проверять корректность данных.
local file = io.open("data.txt", "r")
if file then
local content = file:read("*all")
-- Проверка на некорректные байты
if string.find(content, "[\128-\191]") then
print("Обнаружены некорректные символы")
end
file:close()
end
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
io.open(),print(),string.find(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Буферизация ввода-вывода
Для повышения производительности при работе с большими объемами данных рекомендуется использовать буферизацию. Функция io.lines() автоматически буферизирует ввод, что ускоряет чтение.
-- Эффективный способ чтения большого файла
for line in io.lines("large_file.txt") do
-- обработка строки
end
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Ключевые вызовы в фрагменте:
io.lines(). - Комментарии после
--поясняют ожидаемый результат; при запуске интерпретатор их игнорирует. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Избегание лишних копий строк
При конкатенации множества строк в цикле лучше использовать таблицу и table.concat, чем оператор ...
-- Неэффективно
local result = ""
for i = 1, 1000 do
result = result .. i
end
-- Эффективно
local parts = {}
for i = 1, 1000 do
table.insert(parts, tostring(i))
end
local result = table.concat(parts, "")
Разбор:
localсоздаёт локальную переменную в текущем блоке; строка в кавычках — литерал типаstring.- Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). - Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - Вызовы из стандартных библиотек
table— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
table.concat(),table.insert(),tostring(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Использование потоков вместо файлов
Для временного хранения данных в памяти предпочтительнее использовать таблицы, а не файлы. Файловый ввод-вывод медленнее работы с памятью.
local cache = {}
for i = 1, 10000 do
cache[i] = generate_data(i)
end
-- Дальнейшая работа с таблицей
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Паттерн "Фабрика файлов"
Создание абстрактного слоя для работы с файлами позволяет легко менять реализацию.
Код ITЗагрузка примера кода…
Разбор:
- Оператор
..склеивает строки (и автоматически приводит числа к строке при конкатенации). if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.setmetatableсвязывает таблицу с метатаблицей; поля__…задают перехват операций (индекс, арифметика, вызов).io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Фигурные скобки
{}создают таблицу: можно задать поляkey = valueи/или элементы-массив с индексами с 1. - Ключевые вызовы в фрагменте:
error(),io.open(),setmetatable(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Паттерн "Логгер"
Централизованная система логирования с возможностью выбора уровня и формата.
Код ITЗагрузка примера кода…
Разбор:
if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
io.open(),os.date(),string.format(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.
Паттерн "Кэш конфигурации"
Хранение часто используемых данных в памяти для ускорения доступа.
Код ITЗагрузка примера кода…
Разбор:
- Цикл
forповторяет тело: в числовой форме перебирает диапазон, в generic — элементы через итератор. if … then … endвыбирает ветку по truthiness: ложными считаются толькоnilиfalse.- Логика записывается словами
and/or/not, а не символами&&/||/!как в C-подобных языках. returnзавершает функцию и может вернуть несколько значений через запятую.io.openоткрывает файл; режим"r"/"w"/"a"задаёт чтение, перезапись или дозапись.- Вызовы из стандартных библиотек
string— готовые функции языка, не нужно писать их с нуля. - Ключевые вызовы в фрагменте:
io.open(),string.match(),tonumber(). - При переносе в свой проект сохраните порядок шагов и проверьте результат через
printили отладчик.