• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

SPOJ - Formularz (błąd wykonania)

Object Storage Arubacloud
0 głosów
260 wizyt
pytanie zadane 20 lutego 2021 w C i C++ przez Billy Użytkownik (680 p.)
edycja 21 lutego 2021 przez Billy

Hejka,

Robiłem zadanie ze SPOJa: https://pl.spoj.com/problems/KC005 
i utknąłem. Dla przykładowych danych program działa super, ale jak puszczam do sprawdzenia, to dostaję błąd wykonania (SIGABRT). Z tego co gdzieś usłyszałem, błąd jest powodowany przez to, że np. odwołujemy się do nieistniejącej szufladki itp. Przeglądając mój kod jednak nie znalazłem niczego takiego. Poniżej zamieszczam kod:

#include <iostream>

using namespace std;

int main()
{
    string II, NN, DATE, x;
    while(cin>>x>>II>>x>>NN>>x>>x>>DATE)
    {

    //IMIE
        if(II[0]>=65 && II[0]<=90 && II.length()<=11)
        {
            for(int i=1; i<II.length()-1; i++)
            {
                if(II[i]<97 || II[i]>122)
                {
                    goto x;
                    continue;
                }
            }
        }
        else
        {
            x:
            cout<<0<<endl;
            continue;
        }
    //NAZWISKO
        if(II[0]>=65 && II[0]<=90 && NN.length()<=21)
        {
            for(int i=1; i<NN.length()-1; i++)
            {
                if(NN[i]<97 || NN[i]>122)
                    goto y;
            }
        }
        else
        {
            y:
            cout<<1<<endl;
            continue;
        }
    //DATA - ROZDZIELENIE
        bool f1=0,f2=0;
        string RRRR,MM,DD;
        for(int i=0; i<10; i++)
        {
            if(DATE[i]=='-' && f1==0) {f1=1; i++;}
            else if(f1==0) {RRRR+=DATE[i]; continue;}

            if(DATE[i]=='-' && f1==1 && f2==0) {f2=1; i++;}
            else if(f1==1 && f2==0) {MM+=DATE[i]; continue;}

            DD+=DATE[i];
        }
    //DATA - WARUNKI
        if(RRRR=="" || MM=="" || DD==""
           || stoi(RRRR)>2000 || stoi(RRRR)<1900 || stoi(MM)>12 || stoi(MM)<1 || stoi(DD)<1 || stoi(DD)>31)
        {
            cout<<2<<endl;
            continue;
        }
    //INNE PRZYPADKI
        cout<<3<<endl;

    }
    return 0;
}

Dla wyjaśnienia te "x" które wczytuję, to po prostu zapychacz, żeby pominąć w jakiś sposób te nagłówki( "imie: " itp.).
Jaki tu może być błąd? I dla jakich jeszcze przypadków może wystąpić błąd wykonania?

komentarz 21 lutego 2021 przez j23 Mędrzec (194,920 p.)

Dla wyjaśnienia te "x" które wczytuję, to po prostu zapychacz, żeby pominąć w jakiś sposób te nagłówki( "imie: " itp.).

W treści zadania masz:

można przyjąć, że pola II, NN, RRRR, MM i DD składają się wyłącznie z czarnych znaków różnych od średnika). 

Czyli założenie, że po : jest biały znak, jest nieprawidłowe.

Użyj std::getline z odpowiednim delimiterem i metody ignore.

komentarz 21 lutego 2021 przez Billy Użytkownik (680 p.)
edycja 21 lutego 2021 przez Billy

można przyjąć, że pola II, NN, RRRR, MM i DD składają się wyłącznie z czarnych znaków różnych od średnika). 

Te pola składają się z takich znaków, okej, ale "imie: " nie jest polem II, tylko jego... nagłówkiem(?) który pomijam wczytując "x". Potem jest spacja, więc następuje wczytywanie do kolejnej zmiennej (w poście faktycznie źle napisałem, pomijam "imie:" a spacja powoduje wczytywanie do nowej zmiennej). 

Chyba że chodziło ci o samo wejście (że jest inne niż podane w zadaniu i nie ma tam spacji), ale to też nie ma sensu, bo po co by były podane testy w tak spreparowanej formie?:

Imie: Roman; Nazwisko: Kowalski6; Data ur.: 1900-01-30
Imie: Andrzej; Nazwisko: Kowal; Data ur.: 1899-10-10
Imie: roman; Nazwisko: No-wak; Data ur.: 1099-11-12
Imie: Alicja; Nazwisko: Nowak; Data ur.: 1990-01-01

Po każdym "nagłówku" jest spacja, więc wykluczyłem możliwość, że źle zrozumiałem wejście. 

Nie wiem o co chodzi :/

P.S. Od razu dodam, że średnik wczytuję do zmiennej, ale pomijam po prostu używając i<length()-1.

1
komentarz 22 lutego 2021 przez j23 Mędrzec (194,920 p.)
edycja 22 lutego 2021 przez j23

Czytanie tak bym zrobił:

std::istream& readRecord(std::istream &is, 
                        std::string &name, 
                        std::string &sname, 
                        std::string &date)
{
    std::getline(is.ignore(666, ':') >> std::ws, name, ';');
    std::getline(is.ignore(666, ':') >> std::ws, sname, ';');
    std::getline(is.ignore(666, ':') >> std::ws, date, '\n');
    return is;    
}


/* ... */

std::string name; 
std::string sname; 
std::string date; 
    
while (readRecord(std::cin, name, sname, date)) {
    /* 
        tu sprawdzasz format danych 
    */
}

Do sprawdzania liter używaj funkcji isalpha, isupper, islower i isdigit.

komentarz 23 lutego 2021 przez Billy Użytkownik (680 p.)
Oj kurcze, nie znam w ogóle tych istream'ów :/ Gdzie można o tym poczytać?
Chociaż wydaje mi się, że da się jakoś poradzić i bez tego, ale zaintrygowało mnie to istream.
komentarz 23 lutego 2021 przez j23 Mędrzec (194,920 p.)

Gdzie można o tym poczytać?

Tu można -> link

Jeszcze odnośnie kodu. Do sprawdzania imienia zrób oddzielną funkcję. Tę samą funkcję możesz użyć do sprawdzenia nazwiska. Nie duplikuj kodu i nie używaj goto.

Do sprawdzenia daty możesz użyć std::istringstream (z std::get_time) albo sscanf.

komentarz 24 lutego 2021 przez Billy Użytkownik (680 p.)

Szczerze nie bardzo zrozumiałem, o co chodzi w tym istream, ale no patrząc na przykłady które podałeś, udało mi się dostosować to do moich potrzeb. Faktycznie, kod stał się znaacznie bardziej czytelny, szczególnie dzięki temu, że rodzielanie daty również zawarłem na początku, podczas wczytywania wszystkich zmiennych. Użyłem też funkcji, które poleciłeś (wiedziałem o nich, no ale wiadomo, na początku warto też taką funkcję samemu zaprogramować :P). Co do goto to wiem, że nie powinno się go używać, ale chciałem oszczędzić właśnie na kodzie (chociaż to oszczędziłbym całą 1 linijkę kodu xd). Sprawdzanie imienia i nazwiska chciałem wpakować do funkcji jako element dopracowania, ale nie działało w ogóle. Jednak wpakowałem do funkcji już teraz, żeby było czytelniejsze. Teraz kod wygląda tak:

#include <iostream>
#include <istream>

using namespace std;

istream& readRecord (istream &is, string &II, string &NN, string &RRRR, string &MM, string &DD)
{
    getline(cin.ignore(666, ':') >> ws, II, ';');
    getline(cin.ignore(666, ':') >> ws, NN, ';');

    getline(cin.ignore(666, ':') >> ws, RRRR, '-');
    getline(cin, MM, '-');
    getline(cin, DD);
    return is;
}

bool checking(string NNorII, int num)
{
 if(isupper(NNorII[0]))
        {
            for(int i=1; i<NNorII.length(); i++)
            {
                if(!islower(NNorII[i]))
                {
                    cout<<num<<endl;
                    return true;
                }
            }
        }
        else
        {
            cout<<num<<endl;
            return true;
        }
        return false;
}

int main()
{

    string II, NN, RRRR, MM, DD;
    while(readRecord(cin,II, NN, RRRR, MM, DD))
    {
    //IMIE
    if(checking(II, 0))
    continue;

    //NAZWISKO
    if(checking(NN,1))
    continue;

    //DATA - WARUNKI
        if(RRRR=="" || MM=="" || DD==""
           || stoi(RRRR)>2000 || stoi(RRRR)<1900 || stoi(MM)>12 || stoi(MM)<1 || stoi(DD)<1 || stoi(DD)>31)
        {
            cout<<2<<endl;
            continue;
        }
    //INNE PRZYPADKI
        cout<<3<<endl;

    }
    return 0;
}

Ale mimo to wyskakuje ten sam błąd. Po skróceniu kodu do widocznej tutaj, jeszcze bardziej czytelnej postaci już zupełnie nie mam pojęcia, dlaczego SPOJ mi to odrzuca (SIGABRT), bo wszystko widać jak na dłoni, a nic nie wydaje się błędne :/

1
komentarz 24 lutego 2021 przez j23 Mędrzec (194,920 p.)

Wracając do pierwotnej wersji readRecord:

bool checkName(const string &name)
{
    if (name.empty() || !isupper(name[0])) return false; 

    for (int i = 1; i < name.length(); i++) {
            if (isalpha(name[i]) && islower(name[i])) continue;
            return false;
        }
    }
    return true;
}

/* ... */

std::string name; 
std::string sname; 
std::string date; 
     
while (readRecord(std::cin, name, sname, date)) {
    if(!checkName(name)) std::cout << "0\n", continue;    
    if(!checkName(sname)) std::cout << "1\n", continue;    
    
    int r, m, d;
    
    if(sscanf(date.c_str(), "%i-%i-%i", &r, &m, &d) != 3 
        | r > 2000 | r < 1900 
        | m > 12 | m < 1 
        | d > 31 | d < 1) std::cout << "2\n", continue;

    std::cout << "3\n";    
}

Jeśli chodzi o checking, staraj się trzymać zasady pojedynczej odpowiedzialności. Funkcja powinna sprawdzić imię i nic więcej nie robić.

komentarz 24 lutego 2021 przez Billy Użytkownik (680 p.)
Podmieniłem sprawdzanie i wczytywanie daty na to twoje, i zaliczyło :D Jednak nadal jestem nieco zmieszany, bo nie mam pojęcia, co było błędem - w sensie wiem, że chodziło o datę, ale dlaczego taki komunikat dostawałem? Jaka linijka go powodowała i czemu? Nie mam pojęcia...
1
komentarz 25 lutego 2021 przez j23 Mędrzec (194,920 p.)

W checking pierwsze co mi się rzuciło to to, że testujesz od razu pierwszą literę, ale nie sprawdzasz, czy w ogóle ta litera tam jest.

 

komentarz 25 lutego 2021 przez Billy Użytkownik (680 p.)

Tylko że checking w ogóle nie zmieniłem. Podmieniłem tylko rzeczy związane z datą i zadziało, dlatego to mnie zdziwiło

komentarz 25 lutego 2021 przez j23 Mędrzec (194,920 p.)
Być może podana data nie zawsze jest w formie czterech znaków na rok i po dwa na miesiąc i dzień. Wtedy ten twój pierwotny kod gmera poza zakresem stringa, co oczywiście musi skończyć się źle.
komentarz 25 lutego 2021 przez Billy Użytkownik (680 p.)
edycja 25 lutego 2021 przez Billy

Właśnie problem, że tam nie ma niczego do gmerania. Nie sięgam nawet do żadnej szufladki z tego stringa:

#include <iostream>
#include <istream>
 
using namespace std;
 
istream& readRecord (istream &is, string &II, string &NN, string &RRRR, string &MM, string &DD)
{
    getline(cin.ignore(666, ':') >> ws, II, ';');
    getline(cin.ignore(666, ':') >> ws, NN, ';');
 
    getline(cin.ignore(666, ':') >> ws, RRRR, '-');
    getline(cin, MM, '-');
    getline(cin, DD);
    return is;
}

/* .... */

//DATA - WARUNKI
    if(RRRR=="" || MM=="" || DD==""
        || stoi(RRRR)>2000 || stoi(RRRR)<1900 || stoi(MM)>12 || stoi(MM)<1 || stoi(DD)<1 || stoi(DD)>31)
    {
        cout<<2<<endl;
        continue;
    }

To mój poprzedni kod, który jeszcze nie działał - wczytałem datę jako całe słowo i zrobiłem te sprawdzanie od ciebie - wszystko działa

P.S. Od razu powiem, że wczytywanie działa idealnie - sprawdzałem poprzez wypisywanie i wszystko wygląda dobrze dla tych testów

1
komentarz 26 lutego 2021 przez j23 Mędrzec (194,920 p.)

Właśnie problem, że tam nie ma niczego do gmerania.

Pisałem o kodzie, który podałeś na samym początku.

stoi rzuci wyjątkiem, jeśli nie będzie mogło przeprowadzić konwersji. Możliwe, że to jest powodem.

P.S. w readRecord masz cin zamiast is.

 

komentarz 26 lutego 2021 przez Billy Użytkownik (680 p.)

stoi rzuci wyjątkiem, jeśli nie będzie mogło przeprowadzić konwersji. Możliwe, że to jest powodem

Myślałem, że jestem mądry, bo przed użyciem tej funkcji sprawdziłem, czy jest puste. Ale jak się przekonałem np. "_10" wyrzuca błąd. Wkurzyłem się więc, i dopiero wtedy zauważyłem, że atoi  robi to samo tylko lepiej. Więc wrzuciłem ją tam, i wszystko zadziałało. W sensie nie wyrzuciło błędu "błąd wykonania", tylko "błędna odpowiedź".
 

I teraz już nie wiem co jest błędne (to, że jak jest spacja przed "-" (np. "1900- 10-10) to nie powinno zaliczać czy jak? już chyba za dużo czasu poświęciłem na to zadanie, a to co najważniejsze się dowiedziałem (czyli dlaczego w ogóle nie działało) no i parę dodatkowych informacji i wskazówek dzięki tobie :P)

A co do readRecord to cin  działa zamiast is (bo zaakceptował rozwiązanie z twoim wczytywaniem daty) (zmienię na wszelki wypadek).  Aczkolwiek nie wiem niestety nadal jak to działa i co to właściwie jest (ta dokumentacja nie wiele mi wyjaśniła). (Chodzi o to, że tworzymy obiekt na podobieństwo cin i korzystamy z niego w ten sam sposób?) Być może jestem na jeszcze zbyt niskim poziomie, żeby to zrozumieć :/

komentarz 26 lutego 2021 przez j23 Mędrzec (194,920 p.)

Chodzi o to, że tworzymy obiekt na podobieństwo cin i korzystamy z niego w ten sam sposób?my obiekt na podobieństwo cin i korzystamy z niego w ten sam sposób?

Nie. is to referencja do strumienia, z którego mają być czytane dane. Jak przy wywołaniu readRecord dasz std::cin, to będzie czytać ze standardowego wejścia, a jak dasz strumień plikowy (obiekt klasy std::ifstream), to będzie czytać z pliku. Tu wykorzystywany jest polimorfizm, dzięki czemu funkcja nie jest uzależniona od jednego źródła danych.

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
1 odpowiedź 105 wizyt
pytanie zadane 5 lipca 2022 w C i C++ przez polandonion Mądrala (7,040 p.)
+1 głos
3 odpowiedzi 1,952 wizyt
pytanie zadane 17 czerwca 2016 w C i C++ przez niezalogowany
+1 głos
1 odpowiedź 129 wizyt
pytanie zadane 4 września 2022 w Java przez Aragedens Obywatel (1,120 p.)

92,625 zapytań

141,483 odpowiedzi

319,825 komentarzy

62,006 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...