Перейти к основному содержимому

Работа со строками, таблицами и файлами

Разработчику Архитектору

Работа со строками, таблицами и файлами

Что такое строка и как она устроена

Строка — это последовательность символов, используемая для представления текстовых данных. В языке программирования 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 или отладчик.

Содержание