Разработчик
Проверенный
Участник
Пользователь
- Сообщения
- 207
- Реакции
- 420
- Помог
- 10 раз(а)
Введение в программирование на SourcePawn. Строки. Форматирование строк.
Форматирование строк позволяет нам собрать строку из некоего шаблона и переменных. Во всех языках оно присутствует. Но чаще используется конкатенация, если имеется.
У Павна конкатенация строк есть только на уровне компилятора, что означает, что все данные должны быть заранее известны.
C++:
stock const char g_szPluginDataString[] = PLUGIN_NAME ... " v" ... PLUGIN_VERSION ... " by " ... PLUGIN_AUTHOR ... " (" ... PLUGIN_URL ... ")";
#define
, и в бинарнике мы уже сможем лицезреть что-то вроде My Awesome Plugin v1.3.3.7 by TopScripterInTheWorld (https://awesomescripter.ru/)
А теперь разберёмся, как собирать динамически строку.
Как я уже сказал в прошлой статье по работе со строками, в SourcePawn имеется аж три функции форматирования строки по шаблону (на самом деле, 4, но разъяснение использования четвёртой отложено до лучших времён):
Что из себя представляет шаблон строки, который принимает абсолютно каждая функция, форматирующая строку? Это смешанная со "специальными тегами" строка, которая в итоге преобразуется в желаемый результат. Пример таких строк:
Привет, %N!
%L забанил %L: %s
%N нашёл %d руб.!
Обнаружено некорректное значение клиентской переменной. Ожидалось %f, получено %f
%
). Если нам понадобится использовать сам знак процента для форматирования, указывается два знака вместо одного (%N выиграл %d рублей с шансом %d%%!
).Доступные "теги" форматирования
%s
- строка%c
- символ%N
- имя игрока, чей индекс был передан%L
- строка с SteamID, UserID и ником игрока, чей индекс был передан. Принято применять в логах. Пример итоговой строки:Kruzya<25><[U:1:111331224]><>
%d
/%i
- число%f
- число с плавающей запятой (float)%b
- бинарное (0
и1
) представление переданной информации (int/char/float/Handle)%x
- шестнадцатеричное представление переданной информации (обычно передают Handle для отладки)%t
- название ключа перевода, которое необходимо подставить%T
- название ключа перевода, которое необходимо подставить + ID игрока, для кого выполняется форматирование
Последние два тега могут принимать бесконечное кол-во аргументов на вход, если того требует фраза из файла переводов. Но в первом случае строка форматируется для глобально установленного игрока (ф-ия SetGlobalTransTarget()), а во втором мы вручную передаём ID клиента после названия ключа и до аргументов форматирования.
Подключение переводов мы рассмотрим в отдельной статье, т.к. эта тема довольно широкая и требует некоторого пояснения.
VFormat()
Эта функция требует отдельного объяснения, пожалуй.
В отличие от Format() / FormatEx(), где мы вручную передаём все аргументы форматирования и т.д., VFormat() оперирует данными из стека вызова функций. Что это означает? VFormat() принято использовать в функциях с заранее неизвестным кол-вом аргументов. Он на вход принимает так же буфер, куда надо записать данные, его размер, строку-шаблон и номер аргумента, с которого надо начинать брать данные для форматирования. Обычно его принято использовать в пользовательских реализациях функции PrintToChat() с некоторыми изменениями. Для примера, реализуем свой PrintToConsoleAll() (его только недавно ввели в SourceMod официально) с поддержкой переводов.
C++:
stock void PrintToConsoleAll(stock const char[] szFormatRules, any ...) { // говорим компилятору, что здесь обязателен только один аргумент.
char szBuffer[256]; // выделяем буфер под результат работы VFormat() заранее
for (int iClient = MaxClients; iClient != 0; --iClient) { // запускаем итерацию по всем игрокам на сервере
// пропускаем всех игроков, которые не в игре, и ботов
if (!IsClientInGame(iClient) || IsFakeClient(iClient))
continue;
// устанавливаем глобально цель для форматирования переводов
SetGlobalTransTarget(iClient);
// выполняем форматирование строки
// обратите внимание: аргументы для форматирования у нас передаются со второго аргумента
VFormat(szBuffer, sizeof(szBuffer), szFormatRules, 2);
// в szBuffer был записан результат форматирования строки, так что просто выведем его.
// во избежание ситуаций, когда в итог попадает любой тег форматирования, принято передавать сам итог как переменную для форматирования %s
PrintToConsole(iClient, "%s", szBuffer);
}
}
PrintToChatAll()
. Он тоже является stock
-функцией в инклудах, только вместо PrintToConsole()
вызывает в итоге PrintToChat()
.Примеры форматирования
Необходимо показать пару примеров, как мне кажется, чтобы закрепить материал:
C++:
char szBuffer[256];
FormatEx(szBuffer, sizeof(szBuffer), "Привет, %N! У Вас %d руб. на счету.", iClient, LK_GetClientMoney(iClient));
// Привет, Kruzya! У Вас 200 руб. на счету.
FormatEx(szBuffer, sizeof(szBuffer), "Client %L called drawing menu with address %x", iClient, hMenu);
// Client Kruzya<25><[U:1:111331224]><> called drawing menu with address a487043d
FormatEx(szBuffer, sizeof(szBuffer), "Admin flags: %b", ADMFLAG_RESERVATION | ADMFLAG_BAN);
// Admin flags: 1001
GetClientAuthId(iClient, AuthId_Steam2, szBuffer, sizeof(szBuffer));
Format(szBuffer, sizeof(szBuffer), "%N, Ваш SteamID v2: %s", iClient, szBuffer);
// Kruzya, Ваш SteamID v2: STEAM_0:0:55665612
Помимо тега типа данных, Мы можем указать ещё некоторые модификаторы: флаг, ширина, точность. Указываются они именно в таком порядке, после знака процента и до тега типа переменной.
Пример:
%03d
. Выведет число тремя цифрами. Если число менее 100, то добавляются нули перед самим числом (013
, 001
).Флаг | Описание |
- | Выключка влево на заданное шириной значение |
+ | Явно указывать знак у числа, даже для положительных чисел |
(пробел) | Если знак не будет выведен, то вставляет пробел перед выводимым числом |
# | Когда используется вместе с x , вставляет перед числом 0 , 0x или 0X Когда используется со спецификаторами f , вставляет десятичную точку, даже если после неё нет десятичных знаков. |
0 | Вставляет нули, когда объявлен спецификатор ширины |
Ширина | Описание |
(число) | Минимальное количество знаков, которое необходимо вывести. Если в числе меньше знаков, то вставляет пробелы (или нули) |
* | Ширина не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимым данным. |
Точность | Описание |
.(число) | Для спецификаторов целых (d , i , x , X ) точность определяет минимальное количество знаков, которое необходимо вывести. Если значение короче, то выводятся нули перед числом. Значение не обрезается, даже если оно длиннее. Точночть 0 означает, что для значения 0 ничего не выводится.Для спецификатора чисел с плавающей точкой ( f ) это число знаков, которые необходимо вывести после десятичной точки (по умолчанию 6).Для s - выводится указанное число символов. По умолчанию выводятся все символы до первого нулевого.Если число не стоит, то по умолчанию точность равна 0 |
* | Точность не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимым данным. |
Последнее редактирование: