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

Punkt, Save, Mecz (zabawa ze strukturami)

Object Storage Arubacloud
0 głosów
501 wizyt
pytanie zadane 4 kwietnia 2020 w C i C++ przez Hubertius Bywalec (2,970 p.)

Cześć

Mam problem z pewnym zadaniem.

Oto jego treść:

Napisz program do zapisywania oraz odczytywania danych ze struktury do oraz z pliku, w postaci tekstowej oraz binarnej.

W tym celu wykorzystaj strukturę point_t oraz funkcje showi set z zadania 4.1 W punkt.

Ponadto przygotuj dodatkowe funkcje do zapisu oraz do odczytu danych struktury point_t, obsługujących format binarny oraz tekstowy przechowywania danych. Funkcje te mają mieć następujące prototypy:

int save_point_b(const char *filename, const struct point_t* p);
int load_point_b(const char *filename, struct point_t* p);
int save_point_t(const char *filename, const struct point_t* p);
int load_point_t(const char *filename, struct point_t* p);
Przyrostki _t oraz _b oznaczają, że pliki mają być zapisywane (save)/odczytywane (load) w trybie/formacie tekstowym oraz binarnym.

Parametry:

filename - nazwa pliku, do którego dana funkcja ma dane zapisać bądź z którego ma dane odczytać,
p (const) - wskaźnik na strukturę point_t, z której funkcja ma pobrać dane do zapisu,
p (non-const) - wskaźnik na strukturę point_t do której funkcja ma zapisać dane po ich odczycie z dysku.
Wartość zwracana:

1 - w przypadku podania błędnych danych podczas wywołąnia funkcji,
2 - w przypadku niemożliwości otworzenia filename,
3 - w przypadku kiedy nie uda się zapisać danych do pliku lub okażą się one uszkodzone,
0 - w przypadku gdy operacja wejścia/wyjścia zakończy się sukcesem.
Format plików

W pliku tekstowym współrzędne powinny być zapisane jako dwie liczby oddzielone od siebie znakiem białym (ang. whitespace).
W pliku binarnym strukturę należy zapisać jako blok pamięci o wielkości zapisywanej struktury.
Napisz program, który przypisze do struktury point_t losowe wartości, a następnie wyświetli je. Nastepnie powinien zapytać użytkownika o nazwę pliku (nie dłuższa niż 30 znaków), do którego zapisana ma zostać utworzona struktura. W zależności od rozszerzenia pliku w podanej nazwie program powinien zapisać strukturę do pliku binarnego (rozszerzenie .bin) lub tekstowego (rozszerzenie .txt).

Jeżeli rozszerzenie będzie błędne to program powinien wyświetlić komunikat Unsupported file format i zwrócić kod błędu 7.
W przypadku niemożliwości utworzenia pliku program powinien wyświetlić komunikat Couldn't create file i zwrócić wartość 5.
Jeżeli udało się zapisać dane do pliku, program powinien wyświetlić komunikat File saved i zapytać użytkownika, czy chce odczytać dane z tego samego pliku. Dopuszczalna odpowiedź to duże lub małe litery y (tak) oraz n (nie).

Odpowiedź TAK: Program powinien odczytać dane i wyświetlić je na ekranie.

Jeżeli nie uda się odczytać danych to program powinien wyświetlić komunikat File corrupted i zwrócić wartość 6.
W przypadku niemożliwości otworzenia pliku program powinien wyświetlić komunikat Couldn't open file i zwrócić wartość 4.
Odpowiedź NIE: Program kończy swoje działanie z kodem błędu 0.

Podanie innego znaku odpowiedzi powinno spowodować wyświetlenie komunikatu Incorrect input i zakończenie programu z kodem błędu 1.

Przykład interkacji z programem -- sukces:

x = 8; y = 7⏎
Podaj sciezke do pliku:⏎
band.txt⏎
File saved⏎
Do you want to read the file (Y/N)? n⏎
x = 4; y = 2⏎
Podaj sciezke do pliku:⏎
room.txt⏎
File saved⏎
Do you want to read the file (Y/N)? y⏎
x = 4; y = 2⏎
x = 9; y = -4⏎
Podaj sciezke do pliku:⏎
second.bin⏎
File saved⏎
Do you want to read the file (Y/N)? y⏎
x = 240; y = -353⏎
Przykładowa interkacja z programem -- błąd danych wejściowych:

x = 8; y = 10⏎
Podaj sciezke do pliku:⏎
self.tx⏎
Unsupported file format
Przykład interakcji z programem -- błąd wejścia/wyjścia:

x = -7; y = -4⏎
Podaj sciezke do pliku:⏎
vary.bin⏎
Couldn't create file⏎
x = -3; y = 4⏎
Podaj sciezke do pliku:⏎
wave.txt⏎
File saved⏎
Do you want to read the file (Y/N)? y⏎
Couldn't open file
Przykładowa interakcja z programem -- plik został uszkodzony między zapisem a odczytem:

x = 7; y = -10⏎
Podaj sciezke do pliku:⏎
mind.txt⏎
File saved⏎
Do you want to read the file (Y/N)? Y⏎
File corrupted
Uwaga

W programie nie wolno używać operatora [], poza deklaracją tablicy na nazwę pliku na 31 elementów (deklaracja tablicy musi wyglądać następująco: nazwa_zmiennej [31]).
W programie nie wolno używać funkcji alokujących pamięć.

A oto i mój kod:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
struct point_t
{
    int x;
    int y;
};

struct point_t* set(struct point_t* p, int x, int y);
void show(const struct point_t* p);

int save_point_b(const char *filename, const struct point_t* p);
int load_point_b(const char *filename, struct point_t* p);
int save_point_t(const char *filename, const struct point_t* p);
int load_point_t(const char *filename, struct point_t* p);

int main()
{
    struct point_t p;
    srand(time(NULL));
    int x=rand()%100,y=rand()%100;
    if(set(&p,x,y)==NULL)
    {
        printf("Incorrect input");
        return 1;
    }
    show(&p);
    printf("\n");

    char tab[31]={0};
    char input[31]={0};
    char *tab_i=&(*(tab+0)), *input_i=&(*(input+0));

    int i;
    printf("Podaj sciezke do pliku:\n");
    scanf(" %31[^\n]", input_i);
    int licznik_on_binary=0;
    int licznik_on_text=0;
    for(i=0; i<30; i++)
    {
        *(tab_i+i)=*(input_i+i);
    }
    for(i=0; i<26; i++)
    {
        if( *(tab_i + i)=='.' && *(tab_i + i + 1)=='b' &&  *(tab_i + i + 2)=='i' &&  *(tab_i + i + 3)=='n' && *(tab_i + i + 4)=='\0')
        {
            licznik_on_binary++;
            //printf("\n%d\n",licznik_on_binary);
            break;
        }
        if( *(tab_i + i)=='.' && *(tab_i + i + 1)=='t' &&  *(tab_i + i + 2)=='x' && *(tab_i + i + 3)=='t' && *(tab_i + i + 4)=='\0')
        {
            licznik_on_text++;
            //printf("\n%d\n",licznik_on_text);
            break;
        }
    }

    if( licznik_on_binary == 0 && licznik_on_text == 0)
    {
        printf("Unsupported file format");
        return 7;
    }


    if( licznik_on_text == 1)
    {
        int result;
        result=save_point_t((const char *) tab,&p);
        if(result==2 || result==3)
        {
            printf("Couldn't create file");
            return 5;
        }
        if(result==0)
        {
            printf("File saved");
        }
        printf("\n");
        char wybor='y';
        printf("Do you want to read the file (Y/N)? ");
        getchar();
        scanf("%c",&wybor);
        if(wybor=='y' || wybor=='Y')
        {
            load_point_t((const char *) tab,&p);
        }
        else if(wybor=='n' || wybor=='N')
        {

        }
        else
        {
            printf("Incorrect input");
            return 1;
        }

    }
    if(licznik_on_binary==1)
    {
        int result;
        result=save_point_b((const char *) tab,&p);
        if(result==2 || result==3)
        {
            printf("Couldn't create file");
            return 5;
        }
        if(result==0)
        {
            printf("File saved");
        }
        printf("\n");
        char wybor='y';
        printf("Do you want to read the file (Y/N)? ");
        getchar();
        scanf("%c",&wybor);
        if(wybor=='y' || wybor=='Y')
        {
            load_point_b((const char *) tab,&p);
        }
        else if(wybor=='n' || wybor=='N')
        {

        }
        else
        {
            printf("Incorrect input");
            return 1;
        }

    }

     return 0;
}

struct point_t* set(struct point_t* p, int x, int y)
{
    if(p==NULL)
    {
        return NULL;
    }
    p->x=x;
    p->y=y;
    return p;
}

void show(const struct point_t* p)
{
    if(p==NULL)
    {
         return ;
    }
    printf("x = %d; y = %d",p->x,p->y);
}


int save_point_b(const char *filename, const struct point_t* p)
{
   if(p==NULL || filename==NULL)
    {
      return 1;
    }
    FILE * f=fopen(filename,"wb");
    if(f==NULL)
    {
        return 2;
    }
    //char znak=' ';
    fwrite(&(p->x),sizeof(p),1,f);
    //fwrite(&znak,sizeof(p),1,f);
    fwrite(&(p->y),sizeof(p),1,f);
    fseek(f,0,SEEK_CUR);
    fclose(f);
    return 0;
}
int load_point_b(const char *filename, struct point_t* p)
{
    if(filename==NULL || p==NULL)
    {
        return 1;
    }
    FILE * f=fopen(filename,"rb");
    if(f==NULL)
    {
        return 2;
    }
    int i=1;
    long long int number_form_fread;
    while(fread(&p,sizeof(p),1,f)==1)
    {
            number_form_fread=(long long int)p;
            if(i==1)
            {
                printf("x = %lli; ",number_form_fread);
            }
            if(i==2)
            {
                printf("y = %lli; ",number_form_fread);
            }
            i++;
    }
    fclose(f);
    return 0;
}
int save_point_t(const char *filename, const struct point_t* p)
{
    if(p==NULL || filename==NULL)
    {
      return 1;
    }
    FILE * f=fopen(filename,"w");
    if(f==NULL)
    {
        return 2;
    }
    fprintf(f,"%d %d",p->x,p->y);
    fseek(f,0,SEEK_SET);
    fclose(f);
    return 0;

}
int load_point_t(const char *filename, struct point_t* p)
{

    if(filename==NULL || p==NULL)
    {
        return 1;
    }
    FILE * f=fopen(filename,"r");
    if(f==NULL)
    {
        return 2;
    }
    int znak;
    while(fscanf(f,"%d",&znak)==1)
    {
        if(znak == p->x)
        {
            printf("x = %d; ",znak);
        }
        if(znak == p->y)
        {
            printf("y = %d",znak);
        }
    }
    fclose(f);
    return 0;
}

Zadanie wysłałem do sprawdzenia i przechodzi mi przez mniej więcej 2/3 testów. Mam jednak pytania do was odnośnie treści zadania, której mam wrażenie do końca nie rozumiem jak i samego rozpisanego przeze mnie kodu.

1)

Tak więc pojawia mi się między innymi na testach komunikat, że "funkcje nie powinny nic wyświetlać". Na pewno nie chodzi więc o funkcję "show". Czyżbym więc coś niepoprawnie zrozumiał w funkcjach load_... i tam nie powinienem nic printfować?

2) 

Na anlizie statycznej pojawiają mi się komunikaty co do niepoprawności poniższego kodu:

sizeof(p)

z opisem: "The code calls sizeof() on a pointer type. This can produce an unexpected result".

Tak więc jak powinienem zapisać to w poprawny sposób, aby uniknąć wszelkich potencjalnych "niebezpieczeństw"?

3)

Nie rozumiem tego momentu w treści zadania dotyczącego opisu co mają zwracać funkcje w tym zadaniu:

3 - w przypadku kiedy nie uda się zapisać danych do pliku lub okażą się one uszkodzone,

To jest właśnie to, przed czym mój program na obecnym etapie nie jest wcale zabezpieczony (głównie dlatego, że nie do końca wiem jakie to mogłyby być przypadki).

4) No i teraz to co mnie najbardziej nurtuje. Testy wywalające się dotyczą funkcji load_point_b i load_point_t. Nie uwzględniając przypadków z return-em 3, dla niektórych w wynikach końcowych wychodzą niepoprawne wartości.

Ot choćby dla load_point_b:

                struct point_t p = { .x = 5, .y = -2};
                struct point_t p1;

                printf("#####START#####");
                int err_code = load_point_b("hundred.bin", &p1);
                printf("#####END#####\n");

                test_error(err_code == 0, "Funkcja load_point_b powinna zwrócić 0, a zwróciła %d", err_code );
                if (!0)
                {
                      test_error(p.x == p1.x, "Funkcja load_point_b odczytała błędne dane, współrzędna x powinna mieć wartość 5, a ma 0", p.x, p1.x );
                      test_error(p.y == p1.y, "Funkcja load_point_b odczytała błędne dane, współrzędna y powinna mieć wartość  -2, a ma -227688824", p.y, p1.y );      
                }

I jeszcze test dla funkcji load_point_t:

Sprawdzanie poprawności działania funkcji load_point_t⏎
#####START##########END#####⏎
Wynik: PORAŻKA: Funkcja load_point_t odczytała błędne dane, współrzędna x powinna mieć wartość 4, a ma 0⏎
       Sprawdź funkcję testującą TEST27(void) z pliku unit_test_v2.c, w linii 1012⏎
Wynik: PORAŻKA: Funkcja load_point_t odczytała błędne dane, współrzędna y powinna mieć wartość -5, a ma -227688824⏎
   
struct point_t p = { .x = 4, .y = -5};
                struct point_t p1;
                printf("#####START#####");
                int err_code = load_point_t("govern.bin", &p1);
                printf("#####END#####\n");
                test_error(err_code == 0, "Funkcja load_point_t powinna zwrócić 0, a zwróciła %d", err_code );
                if (!0)
                {
                      test_error(p.x == p1.x, "Funkcja load_point_t odczytała błędne dane, współrzędna x powinna mieć wartość %d, a ma %d", p.x, p1.x );
                      test_error(p.y == p1.y, "Funkcja load_point_t odczytała błędne dane, współrzędna y powinna mieć wartość %d, a ma %d", p.y, p1.y );      
                }

Chyba wyszczególniłem wszystkie problemy, z którymi borykam się w tym zadaniu. Z góry dziękuję za wszelkie odpowiedzi, dzięki którym będę w stanie polepszyć swój kod.

1 odpowiedź

0 głosów
odpowiedź 4 kwietnia 2020 przez j23 Mędrzec (194,920 p.)

1.

Czyżbym więc coś niepoprawnie zrozumiał w funkcjach load_... i tam nie powinienem nic printfować?

A dlaczego miałyby coś wypisywać na standardowe wyjście? Mają zwracać kody błędów, jeśli coś pójdzie nie tak.

2. sizeof(*p)

3. w save_point_t/b powinieneś zwrócić 3, jeśli f będzie NULL.

4. popraw ten sizeof.

komentarz 4 kwietnia 2020 przez Hubertius Bywalec (2,970 p.)
edycja 4 kwietnia 2020 przez Hubertius

1 i 2 już poprawiłem.

Co do 3):

Gdy rozpisałem w funkcjach save przy próbie utworzenia pliku:

FILE * f=fopen(filename,"wb");
    if(f==NULL)
    {
        return 3;
    }
 FILE * f=fopen(filename,"w");
    if(f==NULL)
    {
        return 3;
    }

wyskakuje mi następujący komunikat na teście:

" Reakcja funkcji save_point_b na brak możliwości utworzenia pliku (fopen zwróci NULL)⏎
#####START##########END#####\nWynik: PORAŻKA: Funkcja save_point_b zwróciła nieprawidłową wartość, powinna zwrócić 2, a zwróciła 3⏎
"

Tak więc czy aby na pewno chodzi o zamianę dla funkcji save na return 3 w przypadku f==NULL?

Dla przypadku, kiedy powinno być return 3 np. dla funkcji load_point_t:

Sprawdzanie poprawności działania funkcji load_point_t⏎
#####START##########END#####⏎
Wynik: PORAŻKA: Funkcja load_point_t powinna zwrócić 3, a zwróciła 0⏎
       Sprawdź funkcję testującą TEST30(void) z pliku unit_test_v2.c, w linii 1126

i podobnie sytucja ma się dla funkcji load_point_b:

TEST 15: Sprawdzanie poprawności działania funkcji load_point_b⏎
#####START##########END#####⏎
Wynik: PORAŻKA: Funkcja load_point_b powinna zwrócić 3, a zwróciła 0⏎
       Sprawdź funkcję testującą TEST15(void) z pliku unit_test_v2.c, w linii 585⏎
⏎

Zapomniałem wcześniej dodać, że ta sytuacja ze zwracaniem kodu błędu 0 zamiast 3 ma miejsce tylko w funkcjach load. Dla funkcji save wszystkie testy przechodzą poprawnie.

4) W jaki sposób mam poprawić sizeof? Nie powinienem więc pobierać całego rozmiaru struktury?

EDIT

Dobra, zrobiłem tą poprawę z seizof dla funkcji load_point_b:

int load_point_b(const char *filename, struct point_t* p)
{
    if(filename==NULL || p==NULL)
    {
        return 1;
    }
    FILE * f=fopen(filename,"rb");
    if(f==NULL)
    {
        return 2;
    }
    int i=1;
    int number_from_fread=0;
    while(fread(&number_from_fread,sizeof(int),1,f)==1)
    {
            if(i==1)
            {
                p->x=number_from_fread;

            }
            if(i==2)
            {
                p->y=number_from_fread;
            }
            i++;
    }
    fseek(f,0,SEEK_SET);
    fclose(f);
    return 0;
}

Teraz wywala tylko na teście z return 3.

W jaki sposób mógłbym jednak zmodyfikować funkcję load_point_t, aby zwiększyć ilość przechodzonych testów?

Ponadto funkcja save_point_t pomimo przechodzenia przez wszystkie testy nie jest do końca dobra, ponieważ dostaje informację, że dla losowo generowanych plików binarnych przy zapisywaniu wychodzą wartości inne od spodziewanych.

komentarz 4 kwietnia 2020 przez j23 Mędrzec (194,920 p.)

W jaki sposób mam poprawić sizeof?

Przecież pokazałem w drugim punkcie. sizeof(p) zwróci wielkość wskaźnika, a nie wielkość struktury point_t. Na 32-bitowej platformie, to będzie za mało.

Spróbuj tak:

int load_point_b(const char* filename, struct point_t* p)
{
    if (filename == NULL || p == NULL) return 1;

    FILE* f = fopen(filename, "rb");
    if (f == NULL) return 2;

    size_t res = fread(p, sizeof(*p), 1, f);
    fclose(f);
    return res == 1 ? 0 : 3;
}

int save_point_b(const char* filename, const struct point_t* p)
{
    if (p == NULL || filename == NULL) return 1;

    FILE* f = fopen(filename, "wb");
    if (f == NULL) return 2;

    size_t res = fwrite(p, sizeof(*p), 1, f);
    fclose(f);
    return res == 1 ? 0 : 3;
}

 

Podobne pytania

0 głosów
1 odpowiedź 123 wizyt
pytanie zadane 25 listopada 2015 w C i C++ przez martix3 Użytkownik (690 p.)
0 głosów
1 odpowiedź 659 wizyt
pytanie zadane 1 kwietnia 2020 w C i C++ przez Hubertius Bywalec (2,970 p.)
0 głosów
3 odpowiedzi 199 wizyt
pytanie zadane 14 stycznia 2016 w C i C++ przez Sebastian R Obywatel (1,110 p.)

92,579 zapytań

141,432 odpowiedzi

319,664 komentarzy

61,965 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!

...