Суббота, 20 Апреля 2024, 04:57

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [355]
Статьи об общих понятиях связанных с созданием игр.
Программирование [82]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [145]
Статьи о программах для создания игр, уроки и описания.
Софт [43]
Различные программы, в том числе в помощь игроделам.
2D-графика [14]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [16]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [161]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [129]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Покупаете ли вы конструкторы игр?
Всего ответов: 11293
Главная » Статьи » Программирование

Lua для всей семьи. Урок 5: работа со строками
Содержание:
1. Урок 1: установка и первые программы
2. Урок 2: условные ветвления и логика
3. Урок 3: циклы и математические функции
4. Урок 4: пользовательские функции

=== Урок №5 ===


Brown paper packages tied up with strings,
These are a few of my favorite things.


В прошлых уроках мы мельком касались строк, и вы уже должны себе представлять, что значение типа строка (string) есть некий текст, который можно, например, вывести на экран; или, говоря более строго, строка - это цепочка символов, каждый из которых представлен в памяти одним или двумя байтами. Строку в Lua можно записать одним из следующих образов:

Код

-- В одинарных кавычках
str = 'Mickey Mouse'

-- В двойных кавычках
str = "stack pile heap"

-- Между так называемыми [i]длинными скобками[/i].
-- Количество знаков равно '=' между двумя открывающими '['
-- и двумя закрывающими ']' знаками, вообще говоря, произвольно.
str1 = [=[Stop spitting on the ground]=]
str2 = [[Prime number]]
str3 = [=====[Windsor McKee]=====]


Большой разницы между ними нет, однако в строке, заключённой в одинарные кавычки разрешено использовать символ ", в строке, заключённой в двойные кавычки разрешён символ ', а в строке в длинных скобках разрешены вообще любые символы, включая перенос строки. Таким образом, длинные скобки лучше всего подходят для записи больших форматированных кусков текста (в следующих примерах за знаком --> будет следовать текст, который выведется на экран).

Код

print "Mah good ol' pal"
--> Mah good ol' pal

print '"This is ridiculous!" he said.'
--> "This is ridiculous!" he said.

print [====[
  O come, O come, Emmanuel,
  and ransom captive Israel
  that mourns in lonely exile here
  until the Son of God appear.
]====]
-- Выведет именно то, что видно


Если очень нужно использовать в строке и ', и ", но не хочется писать длинные скобки (вас можно понять: длинные скобки действительно некрасиво выглядят), то желаемый символ экранируется знаком \ (обратная косая черта):

Код

print "Can use both \'s and \"s."
--> Can use both 's and "s

-- Также можно экранировать перенос строки.
-- Если убрать \ в строке ниже, программа не будет работать.

print 'Hi,\
Eugene.'

--> Hi,
--> Eugene.

-- Если требуется записать в строке непосредственно символ \,
-- он пишется два раза подряд. Это не очень удобно, но по-другому
-- нельзя: либо так, либо используйте длинные скобки.

print "C:\\Lua\\lua.exe"
--> C:\Lua\lua.exe

print [[D:\Photos\smile.jpg]]
--> D:\Photos\smile.jpg


Записи типа \", \' и \\ называются экранирующими последовательностями или escape-последовательностями. Lua поддерживает и другие экранирующие последовательности, которые вы можете использовать в ваших строках:

Код

-- \n - переход на новую строку, как если бы вы
-- нажали Enter в текстовом редакторе:

print "Great\nNews!"
--> Great
--> News!

-- \b - символ backspace.
-- Работу этого символа можно рассмотреть как перемещение
-- виртуального указателя на одну позицию влево, так что дальнейший
-- текст выводится с новой позиции, замещая все последующие символы.
-- Замещаемые символы фактически никуда не деваются, но на экран не
-- выводятся.

print 'You are dumb\b\b\b\b\b nice'
--> You are nice

-- \r - возврат каретки.
-- Виртуальный указатель как бы перемещается в самое начало строки и
-- последующие символы выводятся на экран оттуда.

print "Nobody hid the facts\rBush "
--> Bush hid the facts

-- \t - табуляция, как если бы вы нажали Tab в
-- текстовом редакторе. С его помощью удобно оформлять таблицы.

print "Hewey\tDewey\tLouie"
print "100$\t200$\t150$"
--> Hewey Dewey Louie
--> 100$ 200$ 150$

-- \v - вертиакальная табуляция, почти как если бы вы нажали
-- клавишу "Вниз" в текстовом редакторе.
-- Используется редко, удобна для вывода стихов Маяковского.

print "Listen,\v if stars are lit\v it means - there is someone who needs it"
-- Стих выведется лесенкой.

-- \f - символ form feed. Аналогичен \v, но может не сработать
-- (собственно, как и сам \v).

-- \a - звуковой сигнал (англ. alert). Предполагается, что попытка
-- вывести этот символ на экран заставит встроенный динамик вашего компьютера
-- издать короткий писк, но так как в современных ОС не принято разрешать
-- звук из PC спикера, то \a в большинстве случаев просто не даст никакого
-- эффекта.

-- \xOO - символ с заданным шестнадцатиричным кодом, где вместо ОО
-- вы должны проставить некое двузначное шестнадцатиричное число.
-- Чтобы узнать код желаемого символа, обратитесь к таблице кодов ASCII,
-- в Интернете её довольно легко найти.

print '\x48\x65\x6C\x6C\x6F\x20\x77\x6F\x72\x6C\x64'
-- Тайная запись на языке хакеров!

-- \dOOO - символ с заданным десятичным кодом. То же самое, что и
-- \xOO, только здесь код символа вводится десятичными цифрами. Диапазон
-- допустимых чисел - от 0 до 255.

print "\d52\d50"
-- Знаменитая программа, записаная строкой выше, на одном суперкомпьютере
-- исполнялась миллион лет.


Если вам всё ещё не совсем понятна работа некоторых экранирующих последовательностей - ничего страшного, потому что вам, скорее всего, в жизни пригодится только \n. Учтите, что запись экранирующих последовательностей в строках, заключённых в длинные скобки, не даст никакого эффекта - экранирующие последовательности между длинными скобками игнорируются!

Код

print [[\n\t\v\b\a]]

--> \n\t\v\b\a


Что же, теперь-то мы умеем записать в строке всё, что угодно, но не слишком ли это скучно? В строках самое интересное не то, что мы можем в них записать, а то, что мы можем с ними сделать.

Одна из ключевых операций над строками - конкатенация, или присоединение. Обозначается в тексте программы знаком "..". Суть работы очень проста:
Код

-- Берём строку...

str1 = "mish"

-- И ещё одну...

str2 = "mash"

-- Соединяем...

str3 = str1 .. str2

-- [s]Варим три минуты[/s] Смотрим, что получилось:

print(str3)
--> mishmash


С помощью оператора ".." к строке можно присоединять не только другие строки, но и числа, что даёт нам удобное средство форматирования вывода:

Код

kittens = 3
print("I have "..kittens.." kittens.")
--> I have 3 kittens

-- Код ниже выведет на экран весьма длинную песенку.
for i = 100, 1, -1 do
  print(i .. " bottles of beer on the wall.")
  print(i .. " bottles of beer.")
  print("Take one down and pass it around.")
  print((i-1) .. " bottles of beer on the wall.")
end

print [==[
No more bottles of beer on the wall.
No more bottles of beer.
Go to the store, buy some more!]==]  


Как видно, получить с помощью конкатенации красивый текст с числами гораздо проще, чем если бы вы передали print несколько аргументов через запятую: так бы получилось, что между числом и остальными знаками слишком большой пробел.

Ещё одна полезная операция над строками - сравнение, осуществляемая, как и сравнение чисел, с помощью операторов >, <, ==, ~=, >= и <=. Тут может возникнуть замешательство: если с равенством/неравенством строк всё ясно (в равных строках последовательность символов совпадает), то как проверить, какая строка больше? По длине? Или по сумме кодов символов? Но в первом случае будут считаться равными строки "Jesus" и "Satan", а во втором - "spoon" и "snoop". Непростая задача, выходит!

Понятно, что строке, в отличие от числа, нельзя поставить в соответсвие какое-либо значение, чтобы можно было однозначно, взглянув на него, сказать: какая строка больше, а какая меньше. Поэтому математики, вдохновляясь великими составителями словарей, придумали лексикографическое сравнение. Словари тут притом, что логика лексикографического сравнения примерно такая: большей считается та строка, которая относительно другой стояла бы ниже, если бы их вносили, например, в алфавитный указатель. Более строгое определение порядка сравнения из Википедии:

Слово a предшествует слову b (a < b), если первые m символов совпадают, а m + 1 символ слова a меньше m + 1 символа слова b.

А вот символы запросто сравниваются по величине ASCII-кода, так что сравнение строк теперь полностью определено. Примеры:

Код

print ("banana" > "apple")
--> true

print ("banana" > "banona")
--> false

print ("Argonaut" > "argonaut")
--> false
-- ASCII-коды прописных букв меньше ASCII-кодов строчных букв

print ("bit" < "bitter")
--> true
-- Если символы в неравнодлинных строках совпадают, меньшей считается
-- более короткая.

print ("Satan" == "Satan")
--> true

print ("Jesus" ~= "Satan")
--> true


Лексикографическое сравнение в основном используется для сортировки строк - именно благодаря нему вы можете в интерактивном телефонном справочнике отсортировать номера по фамилиям и быстро найти нужного человека.

Большинство оставшихся вохможных манипуляций со строками в Lua возложено на модуль string: он, как и модуль math, содержит некоторые функции, обращаться к которым необходимо с предшествующим именем модуля, отделённым точкой. Существование модуля string помимо всего прочего обязывает вас не называть ни одну переменную именем string, иначе доступ к модулю для программы будет потерян ценой создания одной маленькой переменной. А ведь, казалось бы, так хочется!

Многие из функций для работы со строками весьма сложны для понимания и использования и не так часто необходимы (например, string.gsub и string.match, а также string.format), поэтому нашего краткого обзора на них ни в коем случае не хватит, но я их всё равно упоминаю здесь, чтобы разжечь ваше любопытство и подтолкнуть вас к самостоятельному изучению их работы с помощью официального руководства, так как в практике программирования на Lua такие знания вполне могут пригодиться. А сейчас мы вкратце рассмотрим простейшие функции модуля string:

1. string.len(<строка>) - возвращает длину строки.
Пример работы:
Код

string.len("")
-- Пустая строка, вернёт 0.

string.len("Yes we can")
-- Вернёт 10.


Длина строки в Lua измеряется в байтах, а не в символах, что на самом деле прискорбно. Между байтом и символом гарантированно нет никакой разницы, если вы используете только символы ASCII, и, в частности, не употребляете русских букв.
Код

string.len("Зело дивно")
-- В кодировке UTF-8 вернёт 19, хоть нам и хотелось бы видеть 10. Зело дивно!
-- Дело в том, что один русский символ представляется двумя байтами, а
-- не одним.
-- Такое поведение может обескуражить программистов на Python, привыкших,
-- что подсчитываются именно действительные символы, в Python подобная
-- функция возвращает 10.


Другой случай, способный вызвать затруднение - экранирующие последовательности. Хоть вы и записываете их двумя символами, в памяти они, скорее всего, будут представлены одним байтом.
Код

string.len("\a\t\b")
-- Возвратит 3.


Исключительное положение в этом смысле у последовательности \n. Дело в том, что перенос строк на разных платформах представлен разными байтами. В Linux это ASCII-символ подача строки с кодом 0xA, в компиьютерах Apple Macintosh - символ возврат каретки с кодом 0xD, а в ОС Windows - это сразу два символа 0xD и 0xA, следующие друг за другом, так что длина строки с последовательностью \n в ней будет иметь разное значение в зависимости от компьютера, на котором запускается программа:
Код

string.len("what a\nhorrible\nnight to\nhave a\ncurse.")
-- Возвратит 39 в Linux и 43 в Windows.


2. string.lower(<строка>) и string.upper(<строка>) - возвращают новую строку, в которой все буквы исходной заменены соответсвенно на строчные либо на заглавные. Заметьте, что исходная строка при этом остаётся в сохранности.
Код

string.upper("A quick brown fox jumps over the lazy dog.")
-- Возвращает: "A QUICK BROWN FOX JUMPS OVER THE LAZY DOG."

string.lower("The NASDA System Was Developed To Protect US")
-- "the nasda system was developed to protect us"


Учтите, что эти функции обычно работают только с латинскими буквами, так что не стоит полагаться на них при работе с русскоязычными символами.

string.toupper и string.tolower удобно применять, если вы хотите сравнить две строки без учёта регистра: просто приведите их к одному регистру и затем производите сравнение.
Код

-- comparestrings.lua --

function compare(str1, str2)
  return (string.toupper(str1) == string.toupper(str2))
end

print( copmpare("StrANgER", "sTRanGer"))
--> true


3. string.reverse(<строка>) - возвращает строку, состоящую из последовательности символов исходной строки, расположенных в обратном порядке.
Грубо говоря, string.reverse "переворачивает" строку.
Код

print(string.reverse("Happy taskbar, can't try."))
--> .yrt t'nac, tabksat yppaH

print(string.reverse("Radar"))
--> radaR


Опять же, так как многобайтовые символы Lua обрабатывает некорректно, использование русских букв в переворачиваемой строке может привести к непредсказуемым результатам. Также в Windows под вопросом корректное переворачивание строк, содержащих символ \n. Следовательно, string.reverse стоит применять с аккуратностью: не думайте, что ("булка" == string.reverse("аклуб")) обязательно возвратит true.

4. string.rep(<исходня строка>, <N>, [строка-разделитель]) - присоединяет друг к другу N копий исходной строки, разделённых строкой-разделителем, если последняя дана.
Код

str = "WOBBLE"
sep = ", "

-- Вызов с разделителем:
print( string.rep(str, 4, sep) )
--> WOBBLE, WOBBLE, WOBBLE, WOBBLE

--Вызов без разделителя:
print( string.rep(str, 4) )
--> WOBBLEWOBBLEWOBBLEWOBBLE

Полезность функции string.rep неочевидна, но мне за всю жизнь удалось пару раз успешно применить её. Правда, лучше бы я этого не делал.

5. string.sub(<строка>, <индекс начального символа>, [индекс конечного символа]) - возвращает подстроку, определяемую заданными индексами.
Упрощённо говоря, так можно обрезать строку.

Код

str = "John Emmanuel Doe"

-- Указав только один индекс, мы велим функции обрезать строку слева до
-- символа с этим индексом:

string.sub(str, 6)
-- Возвратит "Emmanuel Doe"

-- Если указанный индекс меньше нуля, то мы указываем оставить только
-- некоторое число последних символов, а остальное - обрезать:

string.sub(str, -3)
-- Возвратит "Doe"

-- Если указать два индекса, символы, заключённые в позициях между ними,
-- будут оставлены, оставшаяся часть строки - отброшена:

string.sub(str, 6, 13)
-- Возвратит "Emmanuel"


6. string.byte(<строка>, [индекс начального символа], [индекс конечного символа]) - возвращает численные значения байтов, составляющих строку. Индексы возвращаемых символов указываются точно таким же образом, как в функции string.sub, только string.byte можно вообще не передавать никаких индексов, и тогда будет возвращено значение только первого байта.
Так как строки бывают достаточно длинными (средняя строка редко бывает короче десяти символов), возвращаемых значений может быть очень много, поэтому разумнее собирать их не в переменные, а в таблицу, которую мы рассмотрим в следующем уроке.

Код

str = "John Emmanuel Doe"

string.byte(str, 1, 4)
-- Возвратит числа: 74, 111, 104, 110 - числовые
-- коды символов 'J', 'o', 'h', 'n'

string.byte(str)
-- Возвратит 74, код символа 'J'

-- Получаем полное представление строки в кодах ASCII.

string.byte(str, 1, string.len(str))
-- Получаем 74, 111, 104, 110, 32, 69, 109, 97, 117,
-- 101, 108, 32, 68, 111, 101


7. string.char([число1, число2, ...]) - "собирает" строку из переданной последовательности кодов. Удобна в связке string.byte, когда вы с её помощью разбиваете строку на коды, как-то обрабатываете их, а потом собираете обратно в строку.
Учтите, что числа должны находиться в пределах от 0 до 255, иначе функция завершит работу с ошибкой.
Код

string.char(0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64)
-- Возвращает строку "Hello world"

string.char()
-- Возвращает пустую строку "".

string.char(74, 111, 104, 110)
-- Возвратит "John"

--! string(1024, 768, 111)
--! Вызовет ошибку, работать не будет.


Напоследок хочется дать вам какую-нибудь интересную программу, включающую в себя манипуляции со строками, но - беда! - занятных задач на обработку строк очень мало, классическая задача о палиндроме в общем виде на Lua, по крайней мере, с помощью известных нам средств, решается слишком громоздко, а в частном случае - без пробелов, пунктуации и специальных символов - слишком тривиально и, следовательно, скучно (но, может, вы попробуете? Условие такое: напишите функцию, которая проверяет, читается ли фраза слева направо точно так же, как и справа налево. Для упражнения точно пригодятся string.reverse и string.upper или string.lower).

Вместо этого я расскажу вам о преобразованиях между строками и другими значениями и как иногда полезно применять эти преобразования.

Допустим, перед вами стоит задача: найти число, которое получается при перестановке цифр заданного числа в обратном порядке. Традиционное решение предполагает множество последовательных целочисленных делений, но Lua предлагает более элегантный подход, буквально в три действия:
Код

-- reversenumber.lua --

function reversenumber(num)
  local str = tostring(num) -- преобразуем число в строку
  str = string.reverse(str) -- переворачиваем строку
  return tonumber(str) -- преобразуем обратно в число и возвращаем
end

print( rn(123), rn(150000), rn(1.33), rn(0.1))
--> 123 51 33.1 1


Ключевыми функциями в работе reversenumber являются tonumber и tostring.

1. tonumber принимает один или два аргумента, первый из которых - строка, содержащая число, а второй, опциональный - основание системы счисления, в которой оно записано (от 2 до 36). Последнее по умолчанию считается равным 10. Системы счисления с основанием большим, чем 10, образуются подобно шестнадцатиричной: недостающие цифры записываются в виде латинских букв. Если строка представляет собой некорректно записанное число, tonumber возвращает nil.

Код

tonumber("700")
-- Возвратит 700

tonumber("ff", 16)
-- 255

tonumber("steiner", 36)
-- 62727809283

tonumber("one hundred")
tonumber("300 Spartans")
-- Вернут nil


2. tostring работает в обратном направлении: она возвращает строковое представление переданного в качестве параметра значения. Причём значением может быть:

Код

-- Число
tostring(128)
-- Вернёт "128"

-- Логическое значение
tostring(1 > 0)
-- "true"

-- nil
tostring(nil)
-- "nil" (строка "nil", а не значение nil)

-- Функция
tostring(tostring)
-- "function: 0x418a20"


Поздравляю! Теперь вы стали настоящим волшебником строк, и можете творить с ними почти всё, что вам вздумается. Веселитесь, потому что, как говорится:

Код

print(string.sub(_VERSION, 1, 3).." is "..string.sub(tostring(print), 1, 3))


Резюме:
1. Мы узнали, как записываются строки в тексте программы.
2. Узнали про экранирующие последовательности, как и для чего их применять.
3. Узнали про лексикографическое сравнение.
4. Выяснили, почему Lua плохо работает с русскими символами, и в каких ситуациях их следует избегать.
5. Узнали о целом ряде полезных функций модуля string.
6. Научились превращать числа и строки друг в друга с помощью функций tonumber и tostring.


Упражнения:
1. Взгляните на функцию reversenumber из reversenumber.lua. При каких значениях параметра она не будет работать?
2. Как вы думаете, как можно написать свою версию string.sub с помощью string.char и string.byte?
3. Напишите программу, которая сосчитывает, сколько раз в строке встречается символ 'a'.
Категория: Программирование | Добавил: -l33t-h4xx- (03 Февраля 2014) | Автор: Андрей Липовалов
Просмотров: 54566 | Комментарии: 8 | Рейтинг: 5.0/21 |
Теги: ЛУА, программирование, работа со строками, для всей семьи, для начинающих, уроки, Урок, Строки, урок 5, LUA
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Lua для всей семьи. Урок 5: работа со строками», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 8
+0-
8 pet4566   (02 Июля 2018 16:38) [Материал]
Ответ на 3-й вопрос:

function a_count(str)
local count=0
for i=1, string.len(str) do
if string.byte(str,i)==string.byte('a') then
count=count+1
end
end
print("'a' repeats here "..count.." times")
end

print('How many times does letter \'a\' repeat in this sentence?')
a_count('How many times does letter \'a\' repeat in this sentence?')

+0-
7 pet4566   (02 Июля 2018 16:11) [Материал]
Ответ на вопрос 2:
Своя версия string.sub через функции

function string_sub(str,i,j)
return print(string.char(string.byte(str, i, j)))
end
string_sub('It\'s my life',2,6)
:'(

+-1-
5 Robinzon787   (08 Февраля 2014 19:57) [Материал]
Robinzon787Не слушай Gavolot'а, надо чтобы еще больше было.

+-1-
4 Gavolot   (08 Февраля 2014 18:49) [Материал]
GavolotБожественная статья. Она просто необходима людям.
Помойму лучше было бы что-то пописать про love или даже INSTEAD, но это, тут ведь по сути все то, что можно прочитать самому в любом яп. В интернете куча работ со строками. Про прошлые статьи вообще молчу.

+0-
6 -l33t-h4xx-   (09 Февраля 2014 13:20) [Материал]
-l33t-h4xx-Прошу прощения. Мне самому иногда кажется, что я слишком интенсивно мусолю очевидное. Буду работать над этим.

+2-
3 Андреич   (05 Февраля 2014 16:49) [Материал]
Андреичспасибо, жду след. уроков..)

+0-
1 aalla   (05 Февраля 2014 09:29) [Материал]
aallaнадеюсь про регулярные выражения будет урок)

+0-
2 -l33t-h4xx-   (05 Февраля 2014 12:15) [Материал]
-l33t-h4xx-Скорее всего, сделаю, потому что вещь действительно порой полезная.

Однако, строго говоря, в Lua нет поддержки настоящих регулярных выражений: есть их урезанная версия под названием patterns (шаблоны). Разработчики языка прокомментировали это тем, что реализация полноценных регулярных выражений потребовала бы вдвое больше строк кода, чем в Lua уже есть.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • MyRPG Master
  • Yogurt3D
  • Voxatron
  • SRPG Studio
  • GINCS Studio
  • Leadwerks
  • Urho3D
  • AresEd
  • GLEngine2D
  • Quest Creator
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2024 Рейтинг