Введение в программирование на SourcePawn. Часть 5.1.

Сообщения
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
*Точность не указана в строке формата, она передаётся отдельно в виде аргумента, который должен предшествовать выводимым данным.
 
Последнее редактирование:

Пользователи, просматривающие эту тему

Сейчас на форуме нет ни одного пользователя.
Сверху Снизу