Skocz do zawartości
bocian941

sscanf 2.0 - pełny opis i przykłady

Rekomendowane odpowiedzi

sscanf 2.0

Wprowadzenie:

Plugin sscanf 2.0 autorstwa Y_Less dodaje do pawn przydatną funkcję znaną może niektórym z C++ lub PHP. sscanf to funkcja rozdzielająca tekst na części według podanych specyfikacji. Części są przypisywane do poszczególnych zmiennych. Początkowo sscanf był dodawany jako "stock" do kodu pawn jednak kod "rozrósł się" do ponad 1000 linijek i autor postanowił zrobić z niego plugin do sa:mp'a. Poradnik ten jest przetłumaczeniem oraz edycją oryginalnego tematu Y_Less'a. Jako autor tłumaczenia nie wyrażam zgody na kopiowanie przetłumaczonej wersji na inne fora. W poradniku starałem się opisać wszystko jak najdokładniej.

Instalacaja:

  • Usuń wszystkie ewentualne pozostałości po kodzie starego sscanf'a (tj. np. stock sscanf(... )
  • Pobierz sscanf.dll lub .so (zależnie od wersji swojego sytemu)
  • W serwer.cfg dodaj "plugins sscanf" lub "plugins sscanf.so" zależnie od wersji systemu
  • W skrypcie/mapie dodaj #include <sscanf2>

Podstawy i zwracanie:

  • Przykłady sscanf'a

Przykładowy kod do użycia w komendzie do dcmd lub zcmd:

if(sscanf(params, "ui", id_gracza, ilosc_gotowki))

{

    return SendClientMessage(playerid, 0xFF0000AA, "Użycie: /givecash <playerid/name> <amount>");

}
oraz przykładowy kod do sprawdzenia czy dany ciąg znaków jest prawidłową wartością INI (zapis w pliku cos=wartosc)
if(sscanf(szFileLine, "p<=>s[8]s[32]", szIniName, szIniValue))

{

printf("Invalid INI format line");

}
  • Zwracanie
-1 - w wypadku jakiegoś błędu; 0 - gdy kod wykona się prawidłowo;
Specyfikatory: Lista dostępnych specyfikatory (litery "u", "i" i "s" w kodach powyżej) znajdują się poniżej.
  • Podstawowe Specyfikatory
[table] Specyfikator(y) Nazwa Przykładowe wartości i, d liczba dziesiętna 1, -12, 28 c znak a, d, $ l wart. logiczna true, false b liczba dwójkowa (bin) 01001, 0b1100 h, x liczba szesnastkowa (hex) 1A, 0x23 o liczba ósemkowa (octal) 045 12 n liczba (dowolna) 42, 0b010, 0xAC, 045 f float 9.75, -1.53, 200.1 g IEEE float 9.75, -1.53, INFINITY, -INFINITY, NAN, NAN_E u użytkownik (gracz/bot) Y_Less, DrunkeR / ID: 0, 12 b bot kierowca_busa / ID: 0, 12 r gracz Y_Less, DrunkeR / ID: 0, 12 [/table]
  • Ciągi znaków (tzw. string'i)
Specyfikator "s" służy do rozdzielania tekstu do tablic w wersji sscanf2 (plugin) musimy podać rozmiar tablicy przykładowo:
new str[] = "coś do sscanfa", str2[32];

sscanf(str, "s[32]", str2);

print(str2);
Powyższy kod dosłownie przeniesie zawartość 'str' do 'str2', w konsoli wyświetli "coś do sscanfa". Inny przykład:
new str[] = "dwa słowa", str2[32];

sscanf(str, "s[32] ", str2);

print(str2);
W powyższym kodzie specjalnie dodałem znaczek "spacji" po [32] dzięki temu w 'str2' mamy teraz samo "dwa" (to samo powinno wyświetlić się w konsoli). Inny praktyczny przykład:
public OnPlayerCommandText(playerid, cmdtext[])

{

    if(!strcmp(cmdtext, "/sscanf", true, 7)) // MUSI być podana długość komedny

    {

        new przed_spacja[16], po_spacji[64], wiadomosc[128];

        sscanf(cmdtext, "s[16] s[64]", przed_spacja, po_spacji); // przypisujemy do po_spacji to co gracz napisze po /sscanf np. "/sscanf by Y_Less", przypisze "by Y_Less" do 'po_spacji';


        format(wiadomosc, 128, "Komenda: %s || Po spacji masz: %s", przed_spacja, po_spacji); // formatowanie i wysyłanie wiadomości

        SendClientMessage(playerid, 0xFFFFFFFF, wiadomosc);

        return 1;

    }

    return 0;

}
Powyższy kod to przykładowa komenda do sscanf bez systemów komend typu zcmd bądź straszy dcmd.
  • Tablice
Jednym z nowych zaawansowanych specyfikatory jest "a", które służy do tworzenia tablic. Składnia jest podobna do stringów, jak widać po tym przykładzie:
new tablica[5];

sscanf("1 2 3 4 5", "a<i>[5]", tablica);
Dzięki zastosowaniu takiego kodu możemy użyć tablica[index] np.
printf("%d", tablica[4]);
wyświetli nam 5 (w programowaniu liczy się od 0).
  • Enumeratory
Jeden z najprzydatniejszych dodatków do sscnaf'a. Pozwala on na rozdzielenie tekstu używając enuma bezpośrednio do tablicy. Przydaje się głównie przy czytaniu plików itp. (do komend raczej nie ;)). Przykład "praktycznie-teoretyczny"
enum pInfo

{

    wiek,

    Float:pozycja_z,

    nick_zwierzaka[24],

    ulubiony_znak

};

new PlayerInfo[MAX_PLAYERS][pInfo];


public OnPlayerConnect(playerid)

{

    new string[128] = "16 99.12 Mruczka D"; // przykładowo może to być linijka z pliku lub mysql'a ale to nie o tym ten poradnik

    sscanf(string, "e<ifs[24]c>", PlayerInfo[playerid]);

}
Po użyciu takiego kodu możemy używać PlayerInfo (znane głównie z GF'a) np. print(PlayerInfo[nick_zwierzaka]); wyświetli nam "Mruczka" w konsoli. Teraz pora na wytłumaczenie kodu:

e - Rozpoczyna typ "enumeratora"

< - otwiera specyfikacje

i - liczba dziesiętna, bo wiek to właśnie taka liczba

f - float, bo pozycja_z w enum'ie to Float:

s[32] - string na 24 znaki bo taki może być najdłuższy nick (tutaj zwierzaka)

c - pojedynczy znak tutaj jest to 'D'

> - zamyka specyfikacje "enum"

  • "Ciche" wartości
Dwa nowe specyfikatory "{" i "}" są używane do tzw. "uciszania" wartości. Ciągi w tych specyfikatorach są czytane i sprawdzane przez sscanf jednak nie są przypisywane do tablic/zmiennych. Na przykład:
new var;

sscanf("42 -100", "{i}i", var);

printf("%d", var);
Wyświetli w konsoli -100 a 42 nie zostanie nigdzie zapisane, przydatne do pobierania wyników z bazy MySQL aby pominąć id i/lub hasło. Przykładowo można używać tego też w enumach (poprzedni pkt.)
enum pInfo

{

    wiek,

    Float:pozycja_z,

    ulubiony_znak

};

new PlayerInfo[MAX_PLAYERS][pInfo];


public OnPlayerConnect(playerid)

{

    new string[128] = "16 99.12 Mruczka D"; // przykładowo może to być linijka z pliku lub mysql'a ale to nie o tym ten poradnik

    sscanf(string, "e<if{s[24]}c>", PlayerInfo[playerid]);

}
Ten przykład pominie imie_zwierzaka
  • Wyszukiwanie wartości
Wyszukiwanie można zastosować aby znaleźć w rozdzielanym tekście dane słowo. Zacznę od przykładu:

main()

{

    new string[64] = "50 a 10 pepsi 76", liczba0, liczba1;

    sscanf(string, "i'pepsi'i", liczba0, liczba1);

    printf("%d\n%d", liczba0, liczba1);

}

Otrzymamy "50(enter)76", znak 'a' liczba 10 oraz samo "pepsi" zostanie pominięte można tę zrobić to w taki sposób:
sscanf(string, "i{cis[10]}i", liczba0, liczba1);
  • Ograniczniki
W sscanf2 domyślnym ogranicznikiem (znakiem rozdzielającym) jest spacja można to zmienić używając specyfikatora "p", przykładowo:
new var0, var1, var2;

sscanf("1,2,3", "p<,>iii", var0, var1, var2);
poprawnie przypisze nam 1 2 i 3 do kolejnych zmiennych var. Kolejny przykład tu z małym użyciem MySQL'a
new tmp[128], zmienna, tablica[32];

mysql_query(...

mysql_store_result();

mysql_fetch_row(tmp);


sscanf(tmp, "p<|>is[32]", zmienna, tablica);

  • Specyfikatory opcjonalne
w sscanf2 każdy specyfikator (nie licząc ' ', { }, oraz p) może mieć wartość domyślną - tzn. opcjonalną przykładowo opcjonalny string to "S" (normalnie "s") a przykładowa liczba to "I" (duże i, normalnie "i").
main()

{

    new str[32] = "", n;

    sscanf(str, "I(17)", n);

    printf("%d", n);

}
W tym przykładzie nie podaliśmy nic w str więc 'n' przybierze domyślną wartość tj. 17. Inny przykład komenda w ZCMD: /kick id opcjonalny powód:

CMD:kick(playerid, params[])

{

    new id, reason[64], wiadomosc[128];


    if(!IsPlayerAdmin(playerid))

        return SendClientMessage(playerid, 0xFF0000AA, "Dozwolone tylko dla Administratorów! Powodzianie..");


    if(sscanf(params, "uS(adminowi nie chce się podać)[64]", id, reason))

        return SendClientMessage(playerid, 0xFFFFFFAA, "/kick [id] [powód:opcjonalnie]");


     format(wiadomosc, 128, "Jakiś admin wyrzucił gracza z ID: %d powód: %s", id, reason); // formatowanie i wysyłanie wiadomości

     SendClientMessageToAll(0xFFFFFFFF, wiadomosc);


     Kick(id);


     return 1;

}

Jeśli wpiszemy "/kick 10" w czacie wyświetli się wiadomość "Jakiś admin wyrzucił gracza z ID: 10 powód: adminowi nie chce się podać"

Download:

download.png

sscanf-0.3R5.zip - Solidfiles

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach
iczba dwójkowa (bin)

chyba liczba

Ciągi znaków (tzw. string'i)

po tym podajesz dwa przykłady z print(tablica); nigdzie nie określasz tej tablicy ale potem mówisz co się wyświetli w konsoli

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach
Enumeratory

w przykładzie podajesz

a<ifs[24]c>

a czy można pomiędzy s[24] a c dać spacje (tak jak wyżej podawałeś by string był pobierany do spacji, bo tak to chyba może pobrać razem z D)

* Wyszukiwanie wartości

tam jest przykład z pepsi, dlaczego tam liczby sa pobierania z początku i końca a nie te które są najbliższe wyrazu pepsi ??

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

co do enumatora sprawdzałem i pobrało mi jeden wyraz, a co do wyszukiwania wartości pobiera pierwszy "i" po czym pomija resztę aż do 'pepsi' oraz "i" po 'pepsi'

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

DrunkeR, jesteś wielki. W końcu poradnik, który wyjaśnił mi kilka spraw, wcześniej przeze mnie nierozumianych.

Btw. nie wiem jak Wy, ale ja nie używam include'a, tylko dodaję sobie native'y gdzieś w skrypcie.

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Od ilu zmiennych opłaca się użyć sscanf?

Przy jednej to wiadomo, że nie trzeba (przynajmniej w przypadku ZCMD, które zwraca to co jest po spacji jako argument params), ale gdy mamy co najmniej dwie zmienne (np. id gracza i kwota do przelewu, lub jakaś liczba + ciąg)?

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Od ilu zmiennych opłaca się użyć sscanf

Od dwóch, według mnie.

@topic: Lepszego poradnika o sscanf'ie nie widziałem, gratuluję chęci napisania ;).

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

/*
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Funkcja: r_SpaceEx
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Opis działania:
Funkcja pobiera ciąg znaków znajdujący się za podanym znakiem
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Parametry:
* string[] - tekst główny
* &index - po której spacji chcemy pobrać tekst (liczone od 0) (musi to być zmienna !)
* outstring[] - tablica dla pobranego tekstu
* size = sizeof(outstring) - ilość bitów w tablicy outstring
* bool:ignorecase = false - ignorowanie wielkości znaków
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Zwracanie: Funkcja nie zwraca szczególnej wartości
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
*/

stock r_SpaceEx(string[], &index, outstring[], size = sizeof(outstring), bool:ignorecase = false)
{
	index = strfind(string, " ", ignorecase, index);
	new idx2 = strfind(string, " ", ignorecase, index+1);
	if(idx2 != -1)
		strmid(outstring, string, index+1, idx2, size);
	else
	    strmid(outstring, string, index+1, strlen(string), size);

	index = idx2;
}
Jak chcesz zmień sobie nazwe na strtok :) A tu przykład:
public OnFilterScriptInit()
{
    new
        idx,
        str[80],
        xd[] = "Ala ma kota a pies ma Ale";

    r_SpaceEx(xd, idx, str);
    print(str);
    r_SpaceEx(xd, idx, str);
    print(str);
    r_SpaceEx(xd, idx, str);
    print(str);
    r_SpaceEx(xd, idx, str);
    print(str);
    r_SpaceEx(xd, idx, str);
    print(str);
    r_SpaceEx(xd, idx, str);
    print(str);
    return 1;
}

Moja funkcja jest ~50% szybsza niż strtok :)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Jak kiedyś testowałem było aż o 50% szybsze, więcej parametrów za jednym razem ? no dobra, ja wole mieć więcej kodu który jest szybszy :)

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

Linki mają się dobrze...

Zawartość pliku .inc ;)

/*
 *  Version: MPL 1.1
 *  
 *  The contents of this file are subject to the Mozilla Public License Version 
 *  1.1 (the "License"); you may not use this file except in compliance with 
 *  the License. You may obtain a copy of the License at 
 *  http://www.mozilla.org/MPL/
 *  
 *  Software distributed under the License is distributed on an "AS IS" basis,
 *  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 *  for the specific language governing rights and limitations under the
 *  License.
 *  
 *  The Original Code is the sscanf 2.0 SA:MP plugin.
 *  
 *  The Initial Developer of the Original Code is Alex "Y_Less" Cole.
 *  Portions created by the Initial Developer are Copyright (C) 2010
 *  the Initial Developer. All Rights Reserved.
 *  
 *  Contributor(s):
 *  
 *  Special Thanks to:
 *  
 *  SA:MP Team past, present and future
 */

native sscanf(const data[], const format[], {Float,_}:...);
native unformat(const data[], const format[], {Float,_}:...) = sscanf;

Udostępnij tę odpowiedź


Odnośnik do odpowiedzi
Udostępnij na innych stronach

  • Przeglądający   0 użytkowników

    Brak zarejestrowanych użytkowników przeglądających tę stronę.

×