PDA

Просмотр полной версии : [Вопрос] Необязательные параметры в sscanf



Josan_Solomon
19.08.2018, 19:57
Hello, World! Подобных вопросов, знаю, много, но именно для своего случая я почему-то ответа не нашел. Слышал про необязательные параметры в sscanf (с заглавными буквами), но не знаю, как правильно их использовать и создать проверку. Может ли кто-нибудь объяснить, или направить на годный мануал/урок, потому что все едва ли затрагиваются этой темы, а я хотел бы понять во всех подробностях. Признаюсь, sscanf я пользовался непростительно мало, поэтому объяснять придется на пальцах. К примеру, команда /veh id color1 color2, которая создает временный тс указанного цвета, а если цвета не указаны, создается машина заранее заданного (допустим, белого)цвета. То есть цвет - это необязательный параметр, и /veh 411 сработает так же, как, допустим, /veh 411 1 1, то есть сообщение о неправильном использовании показывается только при /veh (т.е. без id транспорта), а при /veh 411, без указания параметров цвета - все должно прекрасно работать.


...Сорри за цистерну воды, пытаюсь исключить недопонимание)) И да, если можно, хотелось бы пример на командном процессоре с синтаксисом zeex'a (по сути, у всех такой. я лично пользуюсь Pawn.CMD, не в обиду Daniel_Cortez xD)

Josan_Solomon
19.08.2018, 21:31
И можно заодно поподробнее про "<, =, >" в sscanf? Если я правильно понял, это едят с массивами, но не понял, как именно

Elrmrnt-Kritik
19.08.2018, 23:07
Мой вопрос по этой же теме. (http://pro-pawn.ru/showthread.php?16036-Работа-со-sscanf2-Неопциональные-параметры)
А что касается угловых скобок, это разделение параметров. По умолчанию между аргументами стоит пробел. И используем что-то вроде "dd". Однако, если между ними будет что-то другое (например, запятая, тире, двоеточие или равно), то в угловых скобках указываем именно этот разделитель.

Так из строки "12.0, 18.82, 28.36" мы можем извлечь три координаты в переменные x,y,z:

sscanf(string, "<,>fff", x, y, z);


А, например, для извлечение чисел из TIMESTAMP формата (2018-08-19 22:00) можно использовать такой вариант:
sscanf(string, "<->ddd <:>dd", year, month, day, hour, minute, second);

Единственное, я не знаю как "живет" sscanf, когда видит пробелы. Потому что данная мною выше строка вполне рабочая, хоть и есть пробел.

[hr]
Рассмотрим чуть другой вариант. Допустим, есть команда /goto:

CMD:goto(playerid, params[])
{
new
Float:x, Float:y, Float:z,
interior, world;

if(sscanf(params, !"<,>fffD(-1)D(-1)", x, y, z, interior, world)){
return SendClientMessage(playerid, -1, !"используйте /goto [x] [y] [z] (интерьер) (виртуальный мир)") & 0;
}

if(interior != -1 && interior != GetPlayerInterior(playerid)){
SetPlayerInterior(playerid, interior);
}
if(world != -1 && world != GetPlayerVirtualWorld(playerid)){
SetPlayerVirtualWorld(playerid, world);
}

SetPlayerPos(playerid, x, y, z);
return 1;
}

В этой команде мы можем не указывать параметры интерьера и виртуального мира. Но если укажем, произойдет изменение интерьера.

Josan_Solomon
20.08.2018, 03:26
Я очень извиняюсь за свою тупость, но не могли бы вы мне, пожалуйста, сказать, почему следующий код крашит сервер:
CMD:veh(playerid, params[])
{
CMD:veh(playerid, params[])
{
if (player_info[playerid][aLevel]<2)
return SCM (playerid, SYSTEM_NO_PERMISSION, "Эта команда доступна только администрации и агентам поддержки");
if ( sscanf(params, "dD(1)D(1)D(0)", params[0], params[1], params[2], params[3])) //Что-то мне подсказывает, что ошибка
//тут, так как не выполняется SCM (дефайн SendClientMessage) ни одного из условий
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh ID цвет 1, цвет 2, наличие сирены(1/0)");
if (399>=params[0]>=601 || 0>params[1]>255 || 0>params[2]>255)
return SCM (playerid, SYSTEM_INCORRECT_USAGE, "Возможные ID транспорта: 400-600. Возможные цвета: 0-255");

SCM(playerid, SYSTEM_SUCCESS, "Готово!");
new Float:x, Float:y, Float:z, Float:r;
GetPlayerPos(playerid, x, y, z);
GetPlayerFacingAngle(playerid, r);
printf ("ID %d COLOR 1:%d 2:%d SIREN %d", params[0], params[1], params[2], params[3]);
new car = CreateVehicle(params[0], x,y,z,r,params[1],params[2], -1, params[3]);
return PutPlayerInVehicle(playerid, car, 0);

}
}
И да, где логи подобных крахов? crashinfo показывает далекие от моего понимания ассемблерные листинги (вроде), а в логах сервера даже printf не отобразился

- - - Добавлено - - -

При вводе чего-то, отличного от "/veh", но все равно неправильного, например "/veh pawno-info хороший ресурс" почему-то не отображается никакой ошибки типа "Используйте /veh pro-pawn top, ги сосатб", хотя сервер не крашит

DeimoS
20.08.2018, 11:49
Мой вопрос по этой же теме. (http://pro-pawn.ru/showthread.php?16036-Работа-со-sscanf2-Неопциональные-параметры)
А что касается угловых скобок, это разделение параметров. По умолчанию между аргументами стоит пробел. И используем что-то вроде "dd". Однако, если между ними будет что-то другое (например, запятая, тире, двоеточие или равно), то в угловых скобках указываем именно этот разделитель.

Так из строки "12.0, 18.82, 28.36" мы можем извлечь три координаты в переменные x,y,z:

sscanf(string, "<,>fff", x, y, z);


А, например, для извлечение чисел из TIMESTAMP формата (2018-08-19 22:00) можно использовать такой вариант:
sscanf(string, "<->ddd <:>dd", year, month, day, hour, minute, second);

Единственное, я не знаю как "живет" sscanf, когда видит пробелы. Потому что данная мною выше строка вполне рабочая, хоть и есть пробел.


Если указать так:

sscanf(string, "P< ,>fff", x, y, z);

sscanf будет принимать в качестве разделителя как пробел (это дефолтный разделитель), так и запятую. Таким образом можно добавить и большее количество разделителей (1 символ - 1 разделитель), которые sscanf будет искать по всей строке.


Я очень извиняюсь за свою тупость, но не могли бы вы мне, пожалуйста, сказать, почему следующий код крашит сервер:
CMD:veh(playerid, params[])
{
CMD:veh(playerid, params[])
{
if (player_info[playerid][aLevel]<2)
return SCM (playerid, SYSTEM_NO_PERMISSION, "Эта команда доступна только администрации и агентам поддержки");
if ( sscanf(params, "dD(1)D(1)D(0)", params[0], params[1], params[2], params[3])) //Что-то мне подсказывает, что ошибка
//тут, так как не выполняется SCM (дефайн SendClientMessage) ни одного из условий
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh ID цвет 1, цвет 2, наличие сирены(1/0)");
if (399>=params[0]>=601 || 0>params[1]>255 || 0>params[2]>255)
return SCM (playerid, SYSTEM_INCORRECT_USAGE, "Возможные ID транспорта: 400-600. Возможные цвета: 0-255");

SCM(playerid, SYSTEM_SUCCESS, "Готово!");
new Float:x, Float:y, Float:z, Float:r;
GetPlayerPos(playerid, x, y, z);
GetPlayerFacingAngle(playerid, r);
printf ("ID %d COLOR 1:%d 2:%d SIREN %d", params[0], params[1], params[2], params[3]);
new car = CreateVehicle(params[0], x,y,z,r,params[1],params[2], -1, params[3]);
return PutPlayerInVehicle(playerid, car, 0);

}
}
И да, где логи подобных крахов? crashinfo показывает далекие от моего понимания ассемблерные листинги (вроде), а в логах сервера даже printf не отобразился

- - - Добавлено - - -

При вводе чего-то, отличного от "/veh", но все равно неправильного, например "/veh pawno-info хороший ресурс" почему-то не отображается никакой ошибки типа "Используйте /veh pro-pawn top, ги сосатб", хотя сервер не крашит

Установи crashdetect и вся информация о краше будет в обычных логах

ORLADOK
20.08.2018, 13:16
Установи crashdetect и вся информация о краше будет в обычных логах

Я может туплю, но разве проблема не в первых четырех строках команды?

CMD:veh(playerid, params[])
{
CMD:veh(playerid, params[])
{

Я не знаю, возможно вам такая команда будет интереснее:

CMD:vehicle(playerid, params[]){
new Float:x, Float:y, Float:z, Float:r, vehicleid = GetPlayerVehicleID(playerid);
// Хотя я использую переменные, но вполне, думаю, можно обойтись и params[]

GetPlayerPos(playerid, x, y, z);
sscanf(params, !"P< ,>A<i>(0, 0,0, 0)[3]", params);

if(!vehicleid && !(400 <= params[0] <= 611)) return
SendClientMessage(playerid, -1, !"Используйте: /vehicle <modelid> <color> <color> <siren>");

switch(params[0]){
case -1..1, 400..611:{}
default: return
SendClientMessage(playerid, -1, !"Ключи <modelid>: -1 - 1, 400 - 611!");
}
if(!(0 <= params[1] <= 255) || !(0 <= params[2] <= 255)) return
SendClientMessage(playerid, -1, !"Ключи <color>: 0-255!");

if(vehicleid){
if(params[0] < 2){
// Коды: -1, 0, 1

new string[3][] = {
!"Ваш транспорт был удален!",
!"Ваш транспорт был починен!",
!"Ваш транспорт был зареспавнен!"
};

switch(params[0]){
case -1: DestroyVehicle(vehicleid);
case 0:{
RepairVehicle(vehicleid);
SetVehicleHealth(vehicleid, 1000.0); // Я не помню вызывается ли она при RepairVehicle или нет...
}
case 1: SetVehicleToRespawn(vehicleid);
}
SendClientMessage(playerid, -1, string[params[0] + 1]);

} else {
GetVehicleZAngle(vehicleid, r);
SetVehicleToRespawn(vehicleid);

vehicleid = CreateVehicle(params[0], x,y,z, r, params[1],params[2], -1, 0);
PutPlayerInVehicle(playerid, vehicleid, 0);

SendClientMessage(playerid, -1, !"Вы создали транспорт, зареспавнив прежний!");
}
} else {
vehicleid = CreateVehicle(params[0], x,y,z, r, params[1],params[2], -1, 0);
PutPlayerInVehicle(playerid, vehicleid, 0);

SendClientMessage(playerid, -1, !"Вы создали транспорт!");
}

return 1;
}

Здесь задействуется массивы, ключ: a и A.
Использование:
a<тип>[размер_массива]
A<тип>(Необязательные, параметры, перечисленные, через, запятую)[размер_массива]

Ну, честно говоря, P< ,> я скопировал у DeimoS'a. Хотел наглядно показать использование массива, хотя правда по-мне проще переменными.

(Я просто не знаю, что можно ответить тут, ибо все уже отписали :D)

И еще, кто напомнит в каких случаях лучше использовать switch, а где if? Где-то была опись, что в разных случаях один другого предпочтительнее.
UPD.
Честно говоря я думал будет надпись "добавлено"...

Josan_Solomon
21.08.2018, 16:03
Я может туплю, но разве проблема не в первых четырех строках команды?

CMD:veh(playerid, params[])
{
CMD:veh(playerid, params[])
{



Я неправильно скопировал пока переносил на форум. У меня самого такого повторения (рекурсией назвать не могу) нет. Что касается if и switch, как писал Daniel Cortez, switch препроцессируется в множество if/else if. Так, выражение


switch (variable)
{
case 1:
return 1;
case 2:
return 1;
case 3:
return 1;
default:
return null;
}


Будет преобразовано в вырежение


if (variable == 1)
return 1;
else if (variable == 2)
return 2;
else if (variable == 3)
return 3;
else
return null;


- - - Добавлено - - -




Установи crashdetect и вся информация о краше будет в обычных логах

Он есть, но молчит. Возможно ли, что я неправильно его установил? В списке он сразу после mysql

Josan_Solomon
21.08.2018, 16:41
Такая проверка вообще правильная?

if ( sscanf(params, "dD(1)D(1)D(0)", params[0], params[1], params[2], params[3]))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh ID цвет 1, цвет 2, наличие сирены(1/0)");

ORLADOK
21.08.2018, 18:02
Такая проверка вообще правильная?

if ( sscanf(params, "dD(1)D(1)D(0)", params[0], params[1], params[2], params[3]))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh ID цвет 1, цвет 2, наличие сирены(1/0)");

params = "411":
http://ihost.pro-pawn.ru/image.php?di=BLEL
params = "":
http://ihost.pro-pawn.ru/image.php?di=WKKG
params = "456 54 23 0":
http://ihost.pro-pawn.ru/image.php?di=N0E3
Сам решай :)

DeimoS
21.08.2018, 19:49
Не стоит забывать, что указание "-1" в качестве цвета приведёт к тому, что авто будет присвоен рандомный цвет. Поэтому гораздо лаконичнее будет в sscanf указать именно "-1", а не "1", дабы было удобнее. А заодно и условие

if(!(0 <= params[1] <= 255) || !(0 <= params[2] <= 255))
изменить, добавив "-1" в границы доступных значений.

И кто тебя, ORLADOK, учил return оставлять на первой строке? :) Это же жутко неудобно + теряется весь смысл, заложенный в перенос.

Да и использование switch тут крайне неоправданно. Особенно в "case -1..1, 400..611:{}"



Он есть, но молчит. Возможно ли, что я неправильно его установил? В списке он сразу после mysql

Он должен стоять первым в списке.

- - - Добавлено - - -


Я очень извиняюсь за свою тупость, но не могли бы вы мне, пожалуйста, сказать, почему следующий код крашит сервер:
CMD:veh(playerid, params[])
{
CMD:veh(playerid, params[])
{
if (player_info[playerid][aLevel]<2)
return SCM (playerid, SYSTEM_NO_PERMISSION, "Эта команда доступна только администрации и агентам поддержки");
if ( sscanf(params, "dD(1)D(1)D(0)", params[0], params[1], params[2], params[3])) //Что-то мне подсказывает, что ошибка
//тут, так как не выполняется SCM (дефайн SendClientMessage) ни одного из условий
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh ID цвет 1, цвет 2, наличие сирены(1/0)");
if (399>=params[0]>=601 || 0>params[1]>255 || 0>params[2]>255)
return SCM (playerid, SYSTEM_INCORRECT_USAGE, "Возможные ID транспорта: 400-600. Возможные цвета: 0-255");

SCM(playerid, SYSTEM_SUCCESS, "Готово!");
new Float:x, Float:y, Float:z, Float:r;
GetPlayerPos(playerid, x, y, z);
GetPlayerFacingAngle(playerid, r);
printf ("ID %d COLOR 1:%d 2:%d SIREN %d", params[0], params[1], params[2], params[3]);
new car = CreateVehicle(params[0], x,y,z,r,params[1],params[2], -1, params[3]);
return PutPlayerInVehicle(playerid, car, 0);

}
}
И да, где логи подобных крахов? crashinfo показывает далекие от моего понимания ассемблерные листинги (вроде), а в логах сервера даже printf не отобразился

- - - Добавлено - - -

При вводе чего-то, отличного от "/veh", но все равно неправильного, например "/veh pawno-info хороший ресурс" почему-то не отображается никакой ошибки типа "Используйте /veh pro-pawn top, ги сосатб", хотя сервер не крашит

Глянул сейчас код команды и спешу поздравить тебя с тем, что тебя погубила собственная лень :) Команда не срабатывает из-за вот этой строки:

if ( sscanf(params, "dD(1)D(1)D(0)", params[0], params[1], params[2], params[3]))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh ID цвет 1, цвет 2, наличие сирены(1/0)");
А именно потому, что когда ты прописываешь просто "/veh model_id", в params существует лишь столько ячеек, сколько требуется для хранения ID модели- то бишь, всего 3 ячейки, так как изначально ID хранится в виде текста. А ты пытаешься обратиться к четвёртой ячейке, которой в этом случае существовать не будет. Соответственно, ты будешь ловить выход за пределы массива :)

В общем, +-так
CMD:veh(playerid, params[])
{
if(player_info[playerid][aLevel] < 2)
return SCM(playerid, SYSTEM_NO_PERMISSION, "Эта команда доступна только администрации и агентам поддержки");

new vehicletype,
color1,
color2,
addsiren;
if(sscanf(params, "iI(-1)I(-1)I(0)", vehicletype, color1, color2, addsiren))
{
SCM(playerid, SYSTEM_INCORRECT_USAGE, "Использование: /veh [Модель] <Цвет_1> <Цвет_2> <Сирена>");
SCM(playerid, SYSTEM_INCORRECT_USAGE, "<Цвет> {FFFFFF}Значение цвета не может быть меньше -1 или больше 255 (-1 для выдачи рандомного цвета)");
SCM(playerid, SYSTEM_INCORRECT_USAGE, "<Сирена> {FFFFFF}0 - Транспорт без сирены | 1 - Транспорт с сиреной");
return 1;
}

if(!(400 <= vehicletype <= 600))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Вы указали невалидный ID модели транспорта. [Возможные ID транспорта: 400-600.]");

if(!(-1 <= color1 <= 255))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Вы указали невалидный ID первого цвета. [Минимальный ID - 0 | Максимальный ID - 255 (\"-1\" - рандомный цвет)]");

if(!(-1 <= color2 <= 255))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Вы указали невалидный ID второго цвета. [Минимальный ID - 0 | Максимальный ID - 255 (\"-1\" - рандомный цвет)]");

if(!(0 <= addsiren <= 1))
return SCM(playerid, SYSTEM_INCORRECT_USAGE, "Вы указали невалидное состояние сирены. [0 - Транспорт без сирены | 1 - Транспорт с сиреной]");

new Float:x,
Float:y,
Float:z,
Float:r;
GetPlayerPos(playerid, x, y, z);
GetPlayerFacingAngle(playerid, r);

if(!IsPlayerInAnyVehicle(playerid))
{
new vehicleid = CreateVehicle(vehicletype, x, y, z, r, color1, color2, -1, addaddsiren);
PutPlayerInVehicle(playerid, vehicleid, 0);
}
else
{
CreateVehicle(vehicletype, x+2.0, y, z, r, color1, color2, -1, addaddsiren);
}

SCM(playerid, SYSTEM_SUCCESS, "Готово!");
return 1;
}

ORLADOK
21.08.2018, 20:52
И кто тебя, ORLADOK, учил return оставлять на первой строке? :) Это же жутко неудобно + теряется весь смысл, заложенный в перенос.
Я писал под форум, чтобы уложиться:

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

А так, чему меня учили сложно сказать. Меня обсирали или давали советы на языке технарей, который я тогда не понимал. Мне же еще пока 17, а тогда вовсе 13+ было =)

У меня код в моем файле записан под себя =)

DeimoS
21.08.2018, 23:49
А теперь прочитай ещё и следующий пункт в той теме ;)

ORLADOK
22.08.2018, 00:15
А теперь прочитай ещё и следующий пункт в той теме ;)

Оно в любом случае в моих inc/pwn не так :D

Josan_Solomon
22.08.2018, 00:24
Точно, выход за границы массива! Как же я сам не додумался за всё это время. Огромное спасибо!!! Одна эта тема ответила сразу на несколько моих вопросов :) И теперь ее можно закрыть. Люблю pro-pawn <3 (Это притом, что я не один год просидел на киберфоруме:) )

Josan_Solomon
22.08.2018, 01:50
Спасибо, разобрался. А почему вы проверяете т/с по GetPlayerVehicleID, а не IsPlayerInAnyVehicle? Это что-то личное, или ваш вариант более ресурсосберегательный?

Argument
22.08.2018, 05:08
Спасибо, разобрался. А почему вы проверяете т/с по GetPlayerVehicleID, а не IsPlayerInAnyVehicle? Это что-то личное, или ваш вариант более ресурсосберегательный?
Абсолютно никакой разницы.
Просто GetPlayerVehicleID вернет 0, если игрок не в транспорте (уникальный идентификатор транспорта начинается с 1).

DeimoS
22.08.2018, 10:20
Вообще там логичнее использовать IsPlayerInAnyVehicle :) Просто сначала код был немного другой и забыл поправить.
Закрыто.