sscanf - wyciągnięcie liczb

dział ogólny

sscanf - wyciągnięcie liczb

Nowy postprzez Koziol » wtorek, 25 maja 2010, 10:58

Za pomocą sscanf staram się wyciągnąć z nazwy pliku numer sezonu i odcinka ale za bardzo mi to nie idzie. Robie to w następujący sposób:
Kod: Zaznacz cały
int c,d;
String FName;
FName="Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi"

sscanf (FName.c_str(),"%[a-zA-z0-9.]%[S]%d%[E]%d%[a-zA-z0-9.]",&c,&d);



Nie wiem czemu za kazdym razem wywala wyjatek dostepu do pamieci.
Avatar użytkownika
Koziol
Intelektryk
Intelektryk
 
Posty: 144
Dołączył(a): niedziela, 13 lipca 2008, 17:36
Podziękował : 8
Otrzymał podziękowań: 2
System operacyjny: Windows XP Pro SP2
Kompilator: C++ Builder
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez polymorphism » wtorek, 25 maja 2010, 11:56

Hmm, a skąd ten format string dla sscanf? Pytam, ponieważ nie jest to konstrukcja standardowa, zatem działać nie musi. Jeśli masz możliwość, użyj wyrażeń regularnych (chodzi o bibliotekę), znacznie pewniejsze rozwiązanie.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Cyfrowy Baron » wtorek, 25 maja 2010, 13:47

Funkcja sscanf chyba tak nie działa i nie wyciągnie ci liczb z łańcucha znaków. Wydaje mi się, że kiedyś stosowałem jakąś podobna funkcję, ale nie mogę sobie teraz przypomnieć.

Proponuję proste rozwiązanie oparte na parsowaniu tekstu. Jeżeli w łańcuchu znaków godzina zawsze występuje po prefiksie ".S" to można wyciągnąć z tego godzinę i minutę:

Kod: Zaznacz cały

struct timeE
{
  
int hour;
  
int min;
};

timeE GetTimeE(String Value)
{
 
timeE Result;

 
int S Value.Pos(".S");

 if(
== 0) return Result;

 
Value Value.Delete(11);

 
int E Value.Pos("E");

 
Result.hour Value.SubString(11).ToInt();

 
Value Value.Delete(1E);

 
Value.Pos(".");

 
Result.min Value.SubString(11).ToInt();

 return 
Result;
}

void __fastcall TForm1::Button6Click(TObject *Sender)
{
 
timeE Result;

 
Result GetTimeE("Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi");

 
int c Result.hour;
 
int d Result.min;

 
Memo1->Lines->Add((String)":" + (String)d);

}
 
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Koziol » wtorek, 25 maja 2010, 14:40

polymorphism napisał(a):Hmm, a skąd ten format string dla sscanf? Pytam, ponieważ nie jest to konstrukcja standardowa, zatem działać nie musi. Jeśli masz możliwość, użyj wyrażeń regularnych (chodzi o bibliotekę), znacznie pewniejsze rozwiązanie.


Skorzystalem z Twojej sugestii i zainstalowałem Regular Expression Component Library jest tylko jeden problem nie mam zupełnie pojęcia jak za pomocą tego komponentu wyciagnac interesujace dane do tej pory napisalem:
Kod: Zaznacz cały
re->Expression="([a-zA-Z0-9\.\-]+)([S])([0-9]+)([E])([0-9]+)([a-zA-Z0-9\.\-]+)";
if(re->ParseExpression()!=RegularExpression::REG_NOERROR)
        ShowMessage("Nieprawidlowe wyrażenie regularne");

        if(OpenDialog1->Execute())
        {
                String asd = OpenDialog1->FileName;
                asd = asd.SubString(asd.LastDelimiter("\\")+1,asd.Length());

                re->StringToMatch = asd;
                if(re->Search())
                ShowMessage("Znaleziono poszukiwany ciąg");
                else
                ShowMessage("Nie znaleziono poszukiwanego ciągu znakow");
        }

ale zupelnie nie wiem co dalej a niestety komponent nie jest zbyt dobrze opisany - przynajmniej nie udalo mi sie nic takiego znalezc.

@CyfrowyBaron
Owa funkcja właśnie do tego służy: http://www.cplusplus.com/reference/clib ... io/sscanf/
Kod: Zaznacz cały
/* sscanf example */
#include <stdio.h>

int main ()
{
  char sentence []="Rudolph is 12 years old";
  char str [20];
  int i;

  sscanf (sentence,"%s %*s %d",str,&i);
  printf ("%s -> %d\n",str,i);
 
  return 0;
}
Avatar użytkownika
Koziol
Intelektryk
Intelektryk
 
Posty: 144
Dołączył(a): niedziela, 13 lipca 2008, 17:36
Podziękował : 8
Otrzymał podziękowań: 2
System operacyjny: Windows XP Pro SP2
Kompilator: C++ Builder
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Cyfrowy Baron » wtorek, 25 maja 2010, 15:49

kozioł napisał(a):Owa funkcja właśnie do tego służy:


To jednak Ty nieuważnie to przeczytałeś lub nie zrozumiałeś.


Odczytuje dane z ciągu znaków i zapisuje je w zależności od parametru formatu w miejscach podanych przez dodatkowe argumenty. Miejsca wskazane przez wszystkie dodatkowe argumenty są wypełnione odpowiednimi typami wartości określonymi w ciągu formatu.



Więc jak widzisz nie służy do wyciągania liczb z ciągu znaków, lecz do odczytywania ciągu znaków w zależności o formatu.


[=%[*][ szerokości] [modyfikatory] typ =][/b]

gdzie:

* - gwiazdka oznacza, że dane mają być pobrane z string str, ale ignorowane, tj. nie są przechowywane.
szerokość - określa maksymalną liczbę znaków do odczytu w obecnej operacji czytania
modyfikatory - Specifies a size different from int (in the case of d , i and n ), unsigned int (in the case of o , u and x ) or float (in the case of e , f and g ) for the data pointed by the corresponding additional argument:
h : short int (for d , i and n ), or unsigned short int (for o , u and x ) h: short int (na d, i oraz n), lub unsigned short int (na o, u, x)
l : long int (for d , i and n ), or unsigned long int (for o , u and x ), or double (for e , f and g ) l: long int (na d, i oraz n), or double (do o, u, x) lub podwójne (dla e, f, g) L : long double (for e , f and g ) L: long double (e, f, g) (za długie nie chce mi się tłumaczyć)

typ - typ znaków określający rodzaj danych, które należy czytać, oraz sposób ich odczytywania


Jak to ma się do Twojego kodu, ano tak:

Kod: Zaznacz cały
char sentence [] = "Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi";
int i, j;

sscanf (sentence, "%*[Greys.Anatomy.] %*[S] %d %*[E] %d", &i, &j);

Memo1->Lines->Add((String)i + " " + (String)j);


Teraz odczyta. Przede wszystkim musisz zachować kolejność pobieranych znaków, więc jeżeli chcesz pominąć Greys.Anatomy. i S to musisz podstawić tam typ z gwiazdką %*typ, ale taki typ [a-zA-z0-9.] nie przejdzie, gdyż ta funkcja nie rozpoznaje takiego typu. Musisz podać albo długość ciągu znaków który ma być pominięty, czyli %*14s, albo %*[Greys.Anatomy.], czyli szerokość ciągu znaków. Potem chcesz pominąć S więc również %*S, ale w związku z wcześniejszym nie ma sensu dzielić tak tego. Dalej masz liczbę którą pobierasz do zmiennej typu int, więc %d, potem pomijasz E, więc %*[E], potem znów zmienna typu int więc %d, a ponieważ więcej nie pobierasz więc resztę pomijasz.

Problem stanowi szerokość, gdybyś zamiast tych kropek miał spacje, to można by ten problem rozwiązać, bez podawania szerokości łańcucha znaków. Trzeba więc zamienić kropki na spacje:

kod dla C++Builder wersja 2007 i wcześniejsze.
Kod: Zaznacz cały

 String tekst 
= "Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi";

 tekst = StringReplace(tekst, ".", " ", TReplaceFlags() << rfReplaceAll).Trim();
 int i, j;

 sscanf (tekst.c_str(), "%*s %*s %*[S] %d %*[E] %d", &i, &j);

 Memo1->Lines->Add((String)+ " " + (String)j);
 


Teraz działa - sprawdź!

Nie łatwo będzie jednak to zautomatyzować, gdyż nie wiesz ile spacji wystąpi przed S, jeżeli tytuł programu składa się z dwóch wyrazów to i spacje będą dwie, wtedy format wygląda tak "%*s %*s", ale przy mniejszej i większej liczbie spacji trzeba odpowiednio modyfikować formant.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Cyfrowy Baron » wtorek, 25 maja 2010, 16:05

Uzupełnienie.

Funkcja sscanf rozpozna typ %*[a-zA-Z.], ale nie rozpozna %*[a-zA-Z0-9.]. Nie wiem jak to można obejść.

Kod: Zaznacz cały

 AnsiString tekst 
"Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi";

 
int ij;

 
sscanf (tekst.c_str(), "%*[a-zA-Z.] %*[S] %d %*[E] %d", &i, &j);
 
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Cyfrowy Baron » wtorek, 25 maja 2010, 16:39

typ [a-zA-Z0-9] jest jednak prawidłowy, to ta kropka stanowi problem, gdyż przy takim zapisie [a-zA-Z0-9.] już typ jest nieprawidłowy.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez polymorphism » wtorek, 25 maja 2010, 16:57

Koziol napisał(a):Skorzystalem z Twojej sugestii i zainstalowałem Regular Expression Component Library jest tylko jeden problem nie mam zupełnie pojęcia jak za pomocą tego komponentu wyciagnac interesujace dane

Nie wiem, nie znam tej biblioteki, ale z tego co wyczytałem w dokumentacji, Search wywołuje zdarzenie OnSearching, jeśli coś będzie pasowało do wzoru.

Kod: Zaznacz cały
"([a-zA-Z0-9\.\-]+)([S])([0-9]+)([E])([0-9]+)([a-zA-Z0-9\.\-]+)" 

A nie tak przypadkiem -> [Ss]([0-9]{1,2})[Ee]([0-9]{1,2}) ? Bo jak rozumiem, szukasz tylko numeru serii i odcinka, cała reszta jest nieistotna.

Wyrażenie można rozszerzyć do:
    [Ss]([0-9]{1,2})[Ee]([0-9]{1,3})|([0-9]{1,2})x([0-9]{1,3})|Season[ .]*([0-9]{1,2})[ .]*Episode[ .]*([0-9]{1,3})
wtedy uwzględniane będą wartości w takiej formie:
    Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi
    Greys.Anatomy.3x20.HDTV.XviD2HD.VTV.avi
    Greys.Anatomy.Season.3.Episode.20.HDTV.XviD2HD.VTV.avi

____
p.s. format regex'ów jest z javy, ale nie powinno być problemów z przeniesieniem ich na inną bibliotekę.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Koziol » wtorek, 25 maja 2010, 17:02

Cyfrowy Baron napisał(a):typ [a-zA-Z0-9] jest jednak prawidłowy, to ta kropka stanowi problem, gdyż przy takim zapisie [a-zA-Z0-9.] już typ jest nieprawidłowy.


przy wyrażeniach regularnych trzeba przed znakami specjalinym . , + - itp użyć znaku \ więc za pewne powinno to wyglądać w sposób następujący: [a-zA-Z0-9\.]
Avatar użytkownika
Koziol
Intelektryk
Intelektryk
 
Posty: 144
Dołączył(a): niedziela, 13 lipca 2008, 17:36
Podziękował : 8
Otrzymał podziękowań: 2
System operacyjny: Windows XP Pro SP2
Kompilator: C++ Builder
    Windows XPFirefox

Re: sscanf - wyciągnięcie liczb

Nowy postprzez Cyfrowy Baron » wtorek, 25 maja 2010, 17:15

Konioł napisał(a):przy wyrażeniach regularnych trzeba przed znakami specjalinym . , + - itp użyć znaku \ więc za pewne powinno to wyglądać w sposób następujący: [a-zA-Z0-9\.]


Nie w tym problem. Niżej wyjaśniam.



Najciemniej jest zawsze pod latarnią.

Problemem jest sam formant a nie typ. Przy zapisie [a-zA-Z0-9.] pod uwagę brane są wszystkie litery, wszystkie cyfry i kropki, a więc przy łańcuchu znaków "Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi" nie zatrzyma się przed literą S, gdyż jest to znak uwzględniany przez typ, podobnie jak kropki i cyfry, dlatego taki typ obejmuje cały łańcuch znaków.

W związku z powyższym sprawa wydaje się na pozór nie do rozwiązania, można to jednak obejść:

Kod: Zaznacz cały

 AnsiString tekst 
= "Greys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi";

 tekst = tekst.Insert(" ",  tekst.Pos(".S"));

 int i, j;

 sscanf (tekst.c_str(), "%*[a-zA-Z0-9.] %*[.S] %d %*[E] %d", &i, &j);
  


Niestety, ta metoda nie będzie skuteczna gdy ciąg znaków będzie miał np. taką konstrukcję "Greys.Sanatomy.S03E20.HDTV.XviD2HD.VTV.avi" jak widać .S pojawia się przed oznaczeniem godziny, więc funkcja Pos zatrzyma się na tym znaku. Można by zastąpić funkcje Pos funkcją LastDelimiter przeszukującą łańcuch znaków od końca, co powinno zminimalizować ryzyko błędu, gdyż ryzyko wystąpienia znaku S na końcu jest znikome. Funkcja LastDelimiter ma jednak to ograniczenie, że obsługuje tylko pojedynczy znak, więc:

Kod: Zaznacz cały

 AnsiString tekst 
= "Gre1ys.Anatomy.S03E20.HDTV.XviD2HD.VTV.avi";

 tekst = tekst.Insert(" ",  tekst.LastDelimiter("S"));

 int i, j;

 sscanf (tekst.c_str(), "%*[a-zA-Z0-9.] %*[S] %d %*[E] %d", &i, &j);
  


Na tym kończą mi się pomysły.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 45 gości

cron