Przesyłanie danych z pliku do struktur.

Odpowiedz Nowy wątek
2011-08-09 19:01
0

Witam mam problem z przesyłaniem danych z pliku do struktur. Mam taki program i chcę poprzez niego przesłać dane z pliku do struktur. Plik wygląda tak:

1 Adam Warzecha 14 10 8 5
2 Dariusz Kolodziej 15 11 6 4
3 Pawel Kowalski 17 11 4 6
4 Krzysztof Suliga 11 8 4 9

I jest problem ponieważ gdy odpalam program wyskakują mi takie krzaczki: http://imageshack.us/photo/my-images/52/53143238.jpg/
Nie wiem czym jest to spowodowane, wydaje mi się, że wszystko robię ok

#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
 
struct gracz
{
    int numer;
    char imienazwisko[41];
    int rzuty;
    int celne;
    int asysty;
    int faule;
    float skutecznosc;
};
 
int main(void)
{
    FILE *wsk;
    int i;
    struct gracz tab[4];
 
    if ((wsk = fopen("plik.dat", "r + b")) == NULL)
    {
        puts("Blad otwarcia pliku");
        exit(1);
    }
    else
    {
        rewind(wsk);
        for (i = 0; i < 4; i++)
        {
            fread(&tab[i], sizeof(struct gracz), 1, wsk);
        }
        for (i = 0; i < 4; i++)
        {
            printf("Numer: %d gracz: %s rzuty: %d celne: %d asysty: %d faule %d, skutecznosc: %.2f\n", tab[i].numer, tab[i].imienazwisko, tab[i].rzuty, tab[i].asysty, tab[i].faule, tab[i].faule, tab[i].skutecznosc);
        }
        fclose(wsk);
    }
    getchar();
    getchar();
    return 0;
}
edytowany 1x, ostatnio: olek1, 2011-08-09 19:03

Pozostało 580 znaków

2011-08-09 19:19
0

Jeżeli plik wygląda tak:

1 Adam Warzecha 14 10 8 5
2 Dariusz Kolodziej 15 11 6 4
3 Pawel Kowalski 17 11 4 6
4 Krzysztof Suliga 11 8 4 9

To nie sądzę, aby dało się go tak łatwo wczytać, tak, jak próbujesz.

fread(&tab[i], sizeof(struct gracz), 1, wsk);

Na C się nie znam, dlatego nie pomogę; po prostu moja uwaga ;)


edytowany 1x, ostatnio: Patryk27, 2011-08-09 19:19

Pozostało 580 znaków

2011-08-09 19:26
Kumashiro
1
olek1 napisał(a)

Witam mam problem z przesyłaniem danych z pliku do struktur. Mam taki program i chcę poprzez niego przesłać dane z pliku do struktur. Plik wygląda tak:

1 Adam Warzecha 14 10 8 5
2 Dariusz Kolodziej 15 11 6 4
3 Pawel Kowalski 17 11 4 6
4 Krzysztof Suliga 11 8 4 9

I jest problem ponieważ gdy odpalam program wyskakują mi takie krzaczki: http://imageshack.us/photo/my-images/52/53143238.jpg/

Źle, źle, źle, no źle no...

olek1 napisał(a)

Nie wiem czym jest to spowodowane, wydaje mi się, że wszystko robię ok

Nie robisz ok. W pliku masz wartości liczbowe jako (nazwijmy to) string, a próbujesz wczytywać jako binarki.

olek1 napisał(a)
struct gracz
{
int numer;
char imienazwisko[41];
int rzuty;
int celne;
int asysty;
int faule;
float skutecznosc;
};

Tu masz strukturę, która przechowuje integer, 41 znaczków, 4 integery i floata.

olek1 napisał(a)
fread(&tab[i], sizeof(struct gracz), 1, wsk);

A to Ci z pliku wczytuje (zakładając, że int i float mają 4 bajty) 65 bajtów (+ pewnie do tego wyrównanie do słowa) i nadpisuje tym strukturę w formie binarnej. Bez patrzenia gdzie tam jest int, char czy float.
Popełniasz następujące błędy:

  1. zlewasz endianess i wielkość typów w zależności od architektury (to niekoniecznie musi mieć znaczenie, np. kiedy się tylko bawisz),
  2. wartości liczbowe wczytujesz jako binarki, zamiast sparsować je z char* do int/float
  3. imię i nazwisko wczytujesz z pliku od offsetu 4 do 45 (zakładając 4 bajty na int), niezależnie od tego jak długie to pole jest w pliku
  4. wczytując ciąg znaków zlewasz bajt zerowy

Jak powinieneś to robić:

  1. wczytujesz linię z pliku
  2. odrzucasz (białe?) spacje z poczatku
  3. jedziesz do pierwszej (białej?) spacji i to, co jest przed nią konwertujesz na int (strtol!)
  4. jedziesz dalej, aż napotkasz znak będący cyfrą (niech Latający Potwór Spaghetti ma Cię w opiece jeśli imię lub nazwisko zawiera cyfry), tę część stripujesz ze spacji na początku i końcu, sprawdzasz czy jest ok (np. nie ma w środku bajtów zerowych, nie jest dłuższe niż 40 bajtów) i ładujesz do tablicy w strukturze, na końcu dając bajt zerowy
  5. resztę splitujesz po (białej?) spacji i konwertujesz na inty (strtol!) i floaty (strtod!)

Wszystko to uważnie sprawdzając wyniki konwersji itp.

Możesz też użyć *scanf(), ale z tym to ostrożnie - Kumashiro 2011-08-09 19:31

Pozostało 580 znaków

2011-08-10 10:58
0

Przemęczyłem się przez ten program i napisałem.;d Działa raczej poprawnie, ale kod to taki napisałem, że chyba żal patrzeć.;d Mam tylko jeszcze jeden problem z tą linijką:

tab[x].skutecznosc = float(tab[x].rzuty)/float(tab[x].celne);

Wyskakuje mi błąd "syntax error type". Nie wiem za bardzo czemu.

Oraz u ostatniego zawodnika w zmiennej u przypisuje wartość 81 zamiast 8. U innych jest poprawnie.

#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
 
struct gracz
{
    int numer;
    char imienazwisko[41];
    int rzuty;
    int celne;
    int asysty;
    int faule;
    float skutecznosc;
};
 
int main(void)
{
    FILE *wsk;
    int i;
    int j;
    int n;
    char c[4];
    char c1[4];
    char c2[4];
    char c3[4];
    char c4[4];
    char in[41];
    int r;
    int u;
    int x;
    int f;
    int a;
    int w;
    char *KONIEC;
    char t[101];
    struct gracz tab[4] = 
    {
        {0, "0", 0, 0, 0, 0, 0},
        {0, "0", 0, 0, 0, 0, 0},
        {0, "0", 0, 0, 0, 0, 0},
        {0, "0", 0, 0, 0, 0, 0}
    };
 
    if ((wsk = fopen("plik.dat", "r + b")) == NULL)
    {
        puts("Blad otwarcia pliku");
        exit(1);
    }
    else
    {
        rewind(wsk);
        for (x = 0; x < 4; x++)
        {
            fgets(t, 100, wsk);
            i = 0; j = 0;
            while(t[i] != ' ')
            {
                c[j] = t[i];
                j++;
                i++;
            }
            n = strtol(c, &KONIEC, 10);
            tab[x].numer = n;
            j = 0;
            i++;
            while(!isdigit(t[i]))
            {
                in[j] = t[i];
                i++;
                j++;
            }
            in[j -1 ] = '\0';
            for (w = 0; w < j; w++)
            {
            tab[x].imienazwisko[w] = in[w];
            }
            j = 0;
            while(t[i] != ' ')
            {
                c1[j] = t[i];
                j++;
                i++;
            }
            r = strtol(c1, &KONIEC, 10);
            tab[x].rzuty = r;
            j = 0;
            i++;
            while(t[i] != ' ')
            {
                c2[j] = t[i];
                j++;
                i++;
            }
            u = strtol(c2, &KONIEC, 10);
            tab[x].celne = u;
            j = 0;
            i++;
            while(t[i] != ' ')
            {
                c3[j] = t[i];
                j++;
                i++;
            }
            a = strtol(c3, &KONIEC, 10);
            tab[x].asysty = a;
            j = 0;
            i++;
            while(t[i] != '\0')
            {
                c4[j] = t[i];
                j++;
                i++;
            }
            f = strtol(c4, &KONIEC, 10);
            tab[x].faule = f;
            tab[x].skutecznosc = float(tab[x].rzuty)/float(tab[x].celne);
        }
        if (fclose(wsk) != 0)
        {
            puts("Blad zamkniecia pliku");
            exit(1);
        }
        for (i = 0; i < 4; i++)
        {
            printf("%d %s %d %d %d %d %.2f\n", tab[i].numer, tab[i].imienazwisko, tab[i].rzuty, tab[i].celne, tab[i].asysty, tab[i].faule, tab[i].skutecznosc);
        }
 
    }
    getchar();
    getchar();
    return 0;
}
edytowany 2x, ostatnio: olek1, 2011-08-10 11:07

Pozostało 580 znaków

2011-08-10 12:35
Kumashiro
1
olek1 napisał(a)

Przemęczyłem się przez ten program i napisałem.;d Działa raczej poprawnie, ale kod to taki napisałem, że chyba żal patrzeć.;d Mam tylko jeszcze jeden problem z tą linijką:

tab[x].skutecznosc = float(tab[x].rzuty)/float(tab[x].celne);

Źle rzutujesz.

tab[x].skutecznosc = (float)tab[x].rzuty / (float)tab[x].celne;
olek1 napisał(a)

Oraz u ostatniego zawodnika w zmiennej u przypisuje wartość 81 zamiast 8. U innych jest poprawnie.

Pokaż plik z danymi.
Co do Twojego kodu, to powinieneś wiedzieć, że funkcje nie gryzą. Wręcz przeciwnie, są bardzo użyteczne. Napisz sobie funkcję parsującą, zamiast powtarzać ten sam kod.

olek1 napisał(a)
FILE *wsk;
int i;
int j;
int n;

Czytelniej jest zapisać to tak:

int i, j, n, r, u, x, f, a, w;

Jeszcze lepiej jest użyć tablicy. Nie potrzebujesz jednak tworzyc tylu zmiennych. Możesz recyklować jedną.

olek1 napisał(a)
struct gracz tab[4] = 
{
{0, "0", 0, 0, 0, 0, 0},
{0, "0", 0, 0, 0, 0, 0},
{0, "0", 0, 0, 0, 0, 0},
{0, "0", 0, 0, 0, 0, 0}
};

To jest zbyt rozwlekłe. Wyzeruj blok pamięci:

memset(tab, 0, rows * cols);

Lepszym rozwiązaniem byłoby tworzenie struktur dynamicznie i ich zerowanie:

s = (struct gracz*)calloc(1, sizeof(struct gracz));

Trzeba tylko pamiętać o zwalnianiu pamięci. Calloc() zaalokuje Ci pamięć pod strukturę i ją wyzeruje. Przy okazji prawidłowo zostanie zainicjowana tablica znaków, czyli bajtami zerowymi (pusty ciąg) zamiast wartością "0" (ciąg "0\0<śmieci>").

olek1 napisał(a)
if ((wsk = fopen("plik.dat", "r + b")) == NULL)
{
puts("Blad otwarcia pliku");
exit(1);
}
else
{ ...

Dla użytkownika bardziej istotna jest informacja dlaczego nie udało się otworzyć pliku. Z takim komunikatem zmuszasz go do zgadywania co jest źle.
W if'ie robisz exit(), zatem blok "else" jest tutaj niepotrzebny.
Zamiast exit(1) użyj "return 1" skoro jesteś w main().

olek1 napisał(a)
rewind(wsk);
for (x = 0; x < 4; x++)

Wczytujesz tylko 4 rekordy z pliku. Może warto byłoby pomyśleć o dynamicznej tablicy, lub (bardzo fajne ćwiczenie C przy okazji) liście powiązanej.

olek1 napisał(a)
i = 0; j = 0;

To możesz także zapisać tak:

i = j = 0;
olek1 napisał(a)
while(t[i] != ' ')
{
c[j] = t[i];
j++;
i++;
}

To prosty i skuteczny sposób, ale mam inną propozycję. Spróbuj wykorzystać funkcję strchr() do znalezienia danego znaku. Teoretycznie ona też skanuje ciąg w podobny sposób, ale jej użycie da zwięźlejszy kod. Samo kopiowanie możesz też wtedy przeprowadzić blokowo, a nie znak po znaku. strchr() wymaga zabawy wskaźnikami, co jest odrobinę bardziej złożone, więc jeśli podejmiesz rękawicę i będziesz miał problemy, to daj znać.

olek1 napisał(a)
n = strtol(c, &KONIEC, 10);
tab[x].numer = n;

Nie obsługujesz błędów. Jeśli w "c" znajduje się ciąg znaków niemożliwy do konwersji na wartość liczbową, strtol() zwraca 0. Warto obsłużyć takie przypadki jako błąd składni pliku z danymi. A jak rozpoznać "dobre" 0 od "złego"? Wskaźnik KONIEC powie Ci w którym miejscu tablicy wejściowej strtol() natknął się na zły bajt. Jeśli "KONIEC" (wskaźnik) ma taką samą wartość jak "c" (też wskaźnik), to znaczy że wartości w tablicy wejściowej są złe. Jeśli "KONIEC" wskazuje na bajt inny niż zerowy, to znaczy że na początku ciągu jest liczba, ale dalej jest już coś innego (dlatego najlepiej jest zestripować ciąg z białych spacji na końcu, bo z poczatkowymi strtol() sobie poradzi). Daj znać jeśli potrzebujesz przykładu.

olek1 napisał(a)
while(t[i] != ' ')
{
c1[j] = t[i];
j++;
i++;
}

Jeszcze jedna uwaga. Zlewasz możliwość wczytania "urwanej" linii. Jedziesz po tablicy w poszukiwaniu spacji, a powinieneś też szukać znaku nowej linii (koniec ciągu wczytanego przez fgets(); uciekasz z pętli) i bajtu zerowego (urwana ostatnia linia w pliku, błędne dane).

olek1 napisał(a)
if (fclose(wsk) != 0)
{
puts("Blad zamkniecia pliku");
exit(1);
}

W przypadku plików otwartych tylko do odczytu, błędy zamknięcia nie mają żadnego znaczenia, więc nie musisz tego sprawdzać. Nic złego się nie stanie jeśli z jakiegoś powodu (chociaż trudno mi sobie takowy wyobrazić w przypadku otwarcia RO) system nie będzie w stanie zamknąć pliku. Dane nie zostaną utracone, gdyż nic w buforze wyjściowym nie ma.

Krotko - dobrze Ci idzie, ale jest jeszcze kilka niewielkich niedociągnięć. W języku C trzeba pamiętać o sprawdzaniu błędów. Ignorowanie ich może prowadzić do problemów, nawet trudnych do zlokalizowania.

Pozostało 580 znaków

2011-08-10 12:42
Kumashiro
1
Kumashiro napisał(a)
olek1 napisał(a)
struct gracz tab[4] = 
{
{0, "0", 0, 0, 0, 0, 0},
{0, "0", 0, 0, 0, 0, 0},
{0, "0", 0, 0, 0, 0, 0},
{0, "0", 0, 0, 0, 0, 0}
};

To jest zbyt rozwlekłe. Wyzeruj blok pamięci:

memset(tab, 0, rows * cols);

Autokorekta. Nie wiem dlaczego uznałem tutaj, że tab przechowuje tablice charów... Oczywiście powinno być:

memset(tab, 0, rows * sizeof(struct gracz));

Pozostało 580 znaków

2011-08-10 13:22
0
Kumashiro napisał(a)
olek1 napisał(a)

Oraz u ostatniego zawodnika w zmiennej u przypisuje wartość 81 zamiast 8. U innych jest poprawnie.

Pokaż plik z danymi.

Plik z danymi taki sam jak na początku:

1 Adam Warzecha 14 10 8 5
2 Dariusz Kolodziej 15 11 6 4
3 Pawel Kowalski 17 11 4 6
4 Krzysztof Suliga 11 8 4 9

Później spróbuje zastosować się do twoich porad i coś zdziałać. Jakbym miał problemy to napiszę.
A tak poza tym to wielkie dzięki za wyczerpującą odpowiedź.

edytowany 1x, ostatnio: olek1, 2011-08-10 13:23

Pozostało 580 znaków

2011-08-10 14:21
Kumashiro
2
olek1 napisał(a)
Kumashiro napisał(a)
olek1 napisał(a)

Oraz u ostatniego zawodnika w zmiennej u przypisuje wartość 81 zamiast 8. U innych jest poprawnie.

Pokaż plik z danymi.

Plik z danymi taki sam jak na początku:

1 Adam Warzecha 14 10 8 5
2 Dariusz Kolodziej 15 11 6 4
3 Pawel Kowalski 17 11 4 6
4 Krzysztof Suliga 11 8 4 9

Problemem tutaj jest to, że kiedy skończysz kopiować znaki do tablicy "c2", nie zakańczasz danych bajtem zerowym (i ogólnie źle to obsługujesz). Jak to wygląda:
Alokujesz sobie czteroelementową tablicę charów. Wygląda ona tak ("[]" to bajt w pamięci, "?" to śmieć czyli bajt o jakiejśtam wartości):

[?][?][?][?]

Kopiując do niej dane nadpisujesz bajty, zatem dla tego pliku tablica ta zmienia się tak:

[8][?][?][?]   /* Pierwsza linia w pliku - "8" */
[1][1][?][?]   /* Druga linia - "11" */
[1][1][?][?]   /* Trzecia linia - "11" */
[8][1][?][?]   /* Czwarta linia - "8" */

Co się zatem dzieje? Ponieważ w żaden sposób nie "czyścisz" tablicy po jej użyciu, zostają w niej wartości z poprzednich zapisów. W przypadku ostatniej linii w tablicy leżały na jej początku dwa bajty: "11" z poprzedniego cyklu. Z pliku odczytałeś tylko jeden bajt "8" i wpisałeś go pod indeksem 0, ale druga "1" wciąż tam sobie była.
Miałeś szczęście, że pierwsza linia miała wartość jednocyfrową, a pozostałe (prócz ostatniej) dwucyfrową, więc problem objawił się jedynie dla ostatniej linii. Dlaczego strtol() nie wyleciał z błędem? Ta funkcja parsuje ciąg do czasu, aż nie natknie się na bajt zerowy, lub znaczek nie będący cyfrą (z wyłączeniem początku, gdzie dopuszczalne są jeszcze znaki "-" i "+"). Zatem poprawnie sparsowała liczby i kiedy doszła do "śmiecia", skończyła pracę i zwróciła co jej się udało sparsować oraz (w "KONIEC") wskaźnik do pierwszego śmiecia. W ostanim cyklu dostała tablicę znaków z "81" (wartość "8" z ostatniej linii oraz "1" z poprzedniego cyklu), więc sparsowała ją jako 81.
Co zrobiłeś źle? Musisz pamiętać, że cstringi (ciągi znaków) w C muszą kończyć się bajtem zerowym oznaczającym koniec. Gdybyś na końcu doklejał bajt zerowy, nadpisałbyś nim wartość z poprzedniego cyklu i strtol() sparsowałby tak, jak tego oczekujesz.

Czyli jeszcze raz:

  • ciągi znaków zawsze zakańczaj bajtem zerowym, jeśli co innego o to nie zadba
  • sprawdzaj strtol() pod kątem błędów (w tym przypadku wskaźnik KONIEC wskazywałby na odpowiednio: drugi, trzeci, trzeci i trzeci bajt w tablicy, a wskazywana przez niego wartość byłaby różna od '\0' i '\n')
  • staraj się zerować tablice znaków, do których będziesz kopiował dane (w tym przypadku i tak miałbyś ten sam objaw, ale chociaż ciągi znaków byłyby prawidłowe).

Pozostało 580 znaków

2011-08-10 19:05
0

Na początku przerobiłem program, zmniejszyłem ilość zmiennych i dodałem te znaki zerowe i program wyświetlał prawidłowo dane. Ale potem to namieszałem chyba.;d Zrobiłem jedną funkcję, ale kompilator zgłasza mi błąd przy tej linijce:

while(t[*x] != ' ' && t[*x] != '\n')

Jeszcze wracając do tego:

memset(tab, 0, rows * cols);

to u mnie podkreśla rows i piszę, że nie zna takiego wyrażenia. W C jeszcze nie doszedłem to wszelkich list, kolejek i struktur dynamicznych, więc też nie wiem dokładnie co ten kod oznacza.

Jeszcze jedna uwaga. Zlewasz możliwość wczytania "urwanej" linii. Jedziesz po tablicy w poszukiwaniu spacji, a powinieneś też szukać znaku nowej linii (koniec ciągu wczytanego przez fgets(); uciekasz z pętli) i bajtu zerowego (urwana ostatnia linia w pliku, błędne dane).

Szukanie znaku nowej linii przydaje się przy odczytywaniu ostatniej wartości, ale nie bardzo rozumiem, w którym mogę natrafić na bajt zerowy. Domyślam się, że może chodzi o to, że przy wczytywaniu ostatniej 4 linii na końcu nie znajduje się znak nowej linii '\n' tylko EOF czyli '\0'?

Nie obsługujesz błędów. Jeśli w "c" znajduje się ciąg znaków niemożliwy do konwersji na wartość liczbową, strtol() zwraca 0. Warto obsłużyć takie przypadki jako błąd składni pliku z danymi. A jak rozpoznać "dobre" 0 od "złego"? Wskaźnik KONIEC powie Ci w którym miejscu tablicy wejściowej strtol() natknął się na zły bajt. Jeśli "KONIEC" (wskaźnik) ma taką samą wartość jak "c" (też wskaźnik), to znaczy że wartości w tablicy wejściowej są złe. Jeśli "KONIEC" wskazuje na bajt inny niż zerowy, to znaczy że na początku ciągu jest liczba, ale dalej jest już coś innego (dlatego najlepiej jest zestripować ciąg z białych spacji na końcu, bo z poczatkowymi strtol() sobie poradzi). Daj znać jeśli potrzebujesz przykładu.

Chyba przykład się przyda, bo nie wiem czy do końca dobrze rozumuję.
Z tym drugim argumentem funkcji strtol to rozumiem, że jeśli w tej tablicy C będą np takie dane: 12f'\0' to ustawi on wskaźnik na to f? A jak np. 124'\0' to na '\0', które jest równoważne bajtowi zerowemu NULL? Nie rozumiem określenia zestripować.;d Znaczy co miałbym z tymi spacjami zrobić? Wydaje mi się, że jednak nigdzie ich nie mam w programie bo pomijam je zawsze przy kopiowaniu z tablicy t[] poprzez instrukcję i++ przed przejściem do funkcji.

Z tym błędem strtol coś takiego zrobiłem:

if (*KONIEC != NULL)
{
    printf ("Blad podczas konwersji\n");
    return 1;
}

Nie rozumiem też za bardzo co ma do rzeczy to czy mamy w tablicy c[] 12f czy 12 jeśli strtol konwertuje znaki do napotkania takiego nie będącego cyfrą. Jakie to ma w takim razie konsekwencje dalej?

Dla użytkownika bardziej istotna jest informacja dlaczego nie udało się otworzyć pliku. Z takim komunikatem zmuszasz go do zgadywania co jest źle.
W if'ie robisz exit(), zatem blok "else" jest tutaj niepotrzebny.
Zamiast exit(1) użyj "return 1" skoro jesteś w main().

Ale w jaki sposób mogę określi co dokładnie spowodowało błąd?

#include<stdio.h>
#include<ctype.h>
#include<string.h>
#include<stdlib.h>
 
int konwert(char t[], int *x);
 
struct gracz
{
    int numer;
    char imienazwisko[41];
    int rzuty;
    int celne;
    int asysty;
    int faule;
    float skutecznosc;
};
 
int main(void)
{
    FILE *wsk;
    int i, k, x, w;
    char in[41], t[101];
    struct gracz tab[4] = 
    {
        {0, "0", 0, 0, 0, 0, 0},
        {0, "0", 0, 0, 0, 0, 0},
        {0, "0", 0, 0, 0, 0, 0},
        {0, "0", 0, 0, 0, 0, 0}
    };
 
    if ((wsk = fopen("plik.dat", "r + b")) == NULL)
    {
        puts("Blad otwarcia pliku");
        return 1;
    }
    else
    {
        rewind(wsk);
        for (x = 0; x < 4; x++)
        {
            fgets(t, 100, wsk);
            i = 0;
            tab[x].numer = konwert(t, &i);
            k = 0;
            i++;
            while(!isdigit(t[i]))
            {
                in[k] = t[i];
                i++;
                k++;
            }
            in[k - 1] = '\0';
            for (w = 0; w < k; w++)
            {
            tab[x].imienazwisko[w] = in[w];
            }
            tab[x].rzuty = konwert(t, &i);;
            i++;
            tab[x].celne = konwert(t, &i);
            i++;
            tab[x].asysty = konwert(t, &i);;
            i++;
            tab[x].faule = konwert(t, &i);
            tab[x].skutecznosc = ((double)tab[x].celne/(double)tab[x].rzuty) * 100;
        }
        if (fclose(wsk) != 0)
        {
            puts("Blad zamkniecia pliku");
            exit(1);
        }
        for (i = 0; i < 4; i++)
        {
            printf("%d %s %d %d %d %d %.2f %%\n", tab[i].numer, tab[i].imienazwisko, tab[i].rzuty, tab[i].celne, tab[i].asysty, tab[i].faule, tab[i].skutecznosc);
        }
 
    }
    getchar();
    getchar();
    return 0;
}
 
int konwert(char t[], int *x)
{
    int j = 0;
    int n;
    int a;
    char c[4];
    char *KONIEC;
 
    while(t[*x] != ' ' && t[*x] != '\n')
    {
        c[j] = t[*x];
        j++;
        *x++;
    }
    c[j] = '\0';
    n = strtol(c, &KONIEC, 10);
    for (a = 0; a < 4; a++)
    {
        c[a] = NULL;
    }
    /*if (*KONIEC != NULL)
    {
        printf ("Blad podczas konwersji\n");
        return 1;
    }*/
    return n;
}
edytowany 2x, ostatnio: olek1, 2011-08-10 19:22
Jeżeli tab jest tablicą statyczną, to możesz zrobić memset(tab,0,sizeof(tab)). Żeby kompilator rozumiał słowa rows i cols, musiałbyś zdefiniować je jako zmienne (lub jakoś inaczej, ale się nie rozdrabniajmy). Nie porównuj znaków do NULL. NULL używa się tylko do wskaźników, i mimo tego, że na większości kompilatorów NULL==0 (=='\0'), to może też być (nie w c++) NULL==(void*) 0, i wtedy kod się nie skompiluje. Dodatkowo jest to zły styl. Ja przy znakach zawsze używam '\0', żeby zaakcentować, że jest to znak (a nie np. pomyłka w stylu zamiast '0' napisałem 0). - Zjarek 2011-08-10 20:30

Pozostało 580 znaków

2011-08-10 23:05
Kumashiro
1
olek1 napisał(a)

Na początku przerobiłem program, zmniejszyłem ilość zmiennych i dodałem te znaki zerowe i program wyświetlał prawidłowo dane. Ale potem to namieszałem chyba.;d Zrobiłem jedną funkcję, ale kompilator zgłasza mi błąd przy tej linijce:

while(t[*x] != ' ' && t[*x] != '\n')

A jaki to konkretnie błąd? Bo u mnie gcc z -Wall i -pedantic nie czepia się tej linii (czepia się za to innych).

olek1 napisał(a)

Jeszcze wracając do tego:

memset(tab, 0, rows * cols);

to u mnie podkreśla rows i piszę, że nie zna takiego wyrażenia. W C jeszcze nie doszedłem to wszelkich list, kolejek i struktur dynamicznych, więc też nie wiem dokładnie co ten kod oznacza.

To nie jest kod stricte dla kolejek i struktur dynamicznych. Funkcja memset() wypełnia zadany obszar pamięci (przekazywany wskaźnikiem, o wielkości przekazanej w trzecim argumencie) daną wartością (drugi argument). W tym przypadku zeruje całą tablicę "tab". "rows" (i zapewne "cols") podkreśla Ci dlatego, że ja podałem to jako przykład. W Twoim kodzie nie ma takiej zmiennej. Patrz też na moje sprostowanie dotyczące tego fragmentu. W Twoim kodzie zamiast "rows" powinna być ilość elementów w tablicy, zaś zamiast "cols" rozmiar tych elementów, czyli "sizeof(struct gracz)".

olek1 napisał(a)

Jeszcze jedna uwaga. Zlewasz możliwość wczytania "urwanej" linii. Jedziesz po tablicy w poszukiwaniu spacji, a powinieneś też szukać znaku nowej linii (koniec ciągu wczytanego przez fgets(); uciekasz z pętli) i bajtu zerowego (urwana ostatnia linia w pliku, błędne dane).

Szukanie znaku nowej linii przydaje się przy odczytywaniu ostatniej wartości, ale nie bardzo rozumiem, w którym mogę natrafić na bajt zerowy. Domyślam się, że może chodzi o to, że przy wczytywaniu ostatniej 4 linii na końcu nie znajduje się znak nowej linii '\n' tylko EOF czyli '\0'?

Nie, chodzi o to, że w pliku może być błąd w postaci bajtu zerowego. Dobrze jest coś takiego wykryć, gdyż w wynikach będzie to widoczne jako "urwana" dana i będziesz się zastanawiał dlaczego nie wczytuje całości. Jestem na to wyczulony odkąd system plików XFS miał manierę zerowania plików (lub ich części) w przypadku nieprawidłowego odmontowania i aplikacja dostała "zajoba" po restarcie maszyny.
Natomiast co do znaku nowej linii to dobrze myślisz. W ostatniej może go nie być.

olek1 napisał(a)

Nie obsługujesz błędów. Jeśli w "c" znajduje się ciąg znaków niemożliwy do konwersji na wartość liczbową, strtol() zwraca 0. Warto obsłużyć takie przypadki jako błąd składni pliku z danymi. A jak rozpoznać "dobre" 0 od "złego"? Wskaźnik KONIEC powie Ci w którym miejscu tablicy wejściowej strtol() natknął się na zły bajt. Jeśli "KONIEC" (wskaźnik) ma taką samą wartość jak "c" (też wskaźnik), to znaczy że wartości w tablicy wejściowej są złe. Jeśli "KONIEC" wskazuje na bajt inny niż zerowy, to znaczy że na początku ciągu jest liczba, ale dalej jest już coś innego (dlatego najlepiej jest zestripować ciąg z białych spacji na końcu, bo z poczatkowymi strtol() sobie poradzi). Daj znać jeśli potrzebujesz przykładu.

Chyba przykład się przyda, bo nie wiem czy do końca dobrze rozumuję.

Mówisz i masz. Program wczytuje dane z stdin i przeprowadza konwersję:

#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
 
int main(int  argc, const char  *argv[]) {
    char            *endptr, buf[128];
    long            value;
 
    errno = 0;
    buf[sizeof(buf) - 1] = '\0';
    fgets(buf, sizeof(buf) - 1, stdin);
    value = strtol(buf, &endptr, 10);
    if ( errno || ((*endptr != '\n') && (*endptr != '\0')) || (endptr == buf) ) {
        fprintf(stderr, "Invalid value: %s\n", buf);
        return 1;
    } else
        fprintf(stdout, "Value: %li, Rest: %s\n", value, endptr);
 
    return 0;
}

Zmienna "errno" w przypadku błędu może też zawierać bardziej szczegółową przyczynę, jeśli chcesz to wykorzystać.

olek1 napisał(a)

Z tym drugim argumentem funkcji strtol to rozumiem, że jeśli w tej tablicy C będą np takie dane: 12f'\0' to ustawi on wskaźnik na to f? A jak np. 124'\0' to na '\0', które jest równoważne bajtowi zerowemu NULL? Nie rozumiem określenia zestripować.;d Znaczy co miałbym z tymi spacjami zrobić? Wydaje mi się, że jednak nigdzie ich nie mam w programie bo pomijam je zawsze przy kopiowaniu z tablicy t[] poprzez instrukcję i++ przed przejściem do funkcji.

Zestripować - od "strip" (usuwać, obdzierać), usunąć spacje. Funkcja to robiąca w różnych językach programowania zazwyczaj nazywa się strip().

olek1 napisał(a)

Z tym błędem strtol coś takiego zrobiłem:

if (*KONIEC != NULL)
{
printf ("Blad podczas konwersji\n");
return 1;
}

Źle. fgets() wczytuje wraz ze znakiem nowej linii, zatem przy ostatniej wartości w linii "KONIEC" wskazuje nie na bajt zerowy, tylko na znak nowej linii. Zobacz w przykładzie wyżej jak to rozwiązać.

olek1 napisał(a)

Nie rozumiem też za bardzo co ma do rzeczy to czy mamy w tablicy c[] 12f czy 12 jeśli strtol konwertuje znaki do napotkania takiego nie będącego cyfrą. Jakie to ma w takim razie konsekwencje dalej?

Masz jakiś plik z danymi. Plik ten ma określony format. Jeśli spodziewasz się liczby, to powinna to być liczba, a np. "12f" oznacza albo błąd danych/formatu, albo błąd parsowania pliku. Musisz na takie rzeczy uważać i odpowiednio reagować (np. komunikatem błędu i zakończeniem przetwarzania pliku). Jeśli to zignorujesz i wykorzystasz właściwość strtol(), możesz wczytać nieprawidłowe dane i uzyskać błędne wyniki obliczeń. Ponieważ nie obsługujesz tego jako błąd, możesz tego nigdy nie wykryć.

olek1 napisał(a)

Dla użytkownika bardziej istotna jest informacja dlaczego nie udało się otworzyć pliku. Z takim komunikatem zmuszasz go do zgadywania co jest źle.
W if'ie robisz exit(), zatem blok "else" jest tutaj niepotrzebny.
Zamiast exit(1) użyj "return 1" skoro jesteś w main().

Ale w jaki sposób mogę określi co dokładnie spowodowało błąd?

W globalnej zmiennej "errno" (musisz zaincludować errno.h) znajduje się kod błędu. Możesz go użyć w funkcjach strerror() czy perror() do wyświetlenia komunikatu z przyczyną błędu. Ale uwaga! Jeśli piszesz pod Windows, to nie wiem jak tam wygląda sprawa z errno i funkcjami. Musisz sprawdzić w dokumentacji.

olek1 napisał(a)
rewind(wsk);

Nie musisz wywoływać rewind() na początku. Po otwarciu pliku w trybie innym niż dopisywanie ("a") kursor zawsze znajduje się na jego początku.

olek1 napisał(a)
tab[x].numer = konwert(t, &i);

Podoba mi się ten pomysł z przekazywaniem indeksu przez wskaźnik. Raczej nie spotykane w takim zastosowaniu, ale na swój sposób eleganckie rozwiązanie.

olek1 napisał(a)
while(!isdigit(t[i]))
{
in[k] = t[i];
i++;
k++;
}
in[k - 1] = '\0';
for (w = 0; w < k; w++)
{
tab[x].imienazwisko[w] = in[w];
}

Można to załatwić jedną pętlą. Nie pilnujesz też zakresów. Jeśli w pliku pole z imieniem i nazwiskiem będzie dłuższe niż 41 znaki, wyjedziesz poza tablicę i zaczniesz pisać po innych zmiennych i/lub dostaniesz SEGFAULT (błąd segmentacji). Pamiętaj, że tablice w C nie pilnują swoich zakresów.

k = i;
while ( !isdigit(t[i]) && (i - k < 41) ) {
    tab[x].imienazwisko[i - k] = t[i];
    i++;
}
tab[x].imienazwisko[i - k] = '\0';
olek1 napisał(a)
int konwert(char t[], int *x)
{
[...]
for (a = 0; a < 4; a++)
{
c[a] = NULL;
}
[...]
}

To jest bardzo złe. Kompilator przy tym szczeka. NULL powinien być wykorzystywany tylko przy wskaźnikach. Nie powinieneś nigdy liczyć na to, że NULL będzie miał wartość 0. Wyżej też napisałeś "bajt zerowy NULL", co także nie jest prawidłowe. NULL to jest pusty wskaźnik, który tak się składa że akurat na x86* ma wartość 0, ale na innych platformach może to być co innego.
Skąd wziąłeś tą czwórkę?
Nie musisz zerować wszytkich komórek do końca. Wystarczy, że po ostatnim znaku ciągu znaków będzie jeden bajt zerowy. Kiedy funkcje operujące na ciągach znaków dotrą do bajtu zerowego, ignorują resztę. '\0' jest terminatorem.

NULL zawsze jest zerem (intem), albo zerem (void*, nie dozwolone pod C++). Dlatego np. poprawny jest idiom if(ptr) ... . - Zjarek 2011-08-11 06:37
Wszędzie, gdzie są stałe nie powinieneś zakładać, że na wszystkich platformach i zawsze będą one miały taką wartość. NULL nie jest tutaj wyjątkiem. if(ptr) jest evil. Powinno być if(ptr!=NULL). - Kumashiro 2011-08-11 12:08
To może się tylko zdarzyć, gdy dana platforma nie jest zgodna ze standardem C. Jest w nim zapewnione, że wskaźnik "do niczego" w porównaniu z intem będzie zachowywał się jak 0. To, że jest to stałą, nie znaczy, że na innej platformie może mieć inną wartość (czyli jest wyjątkiem od twojego stwierdzenia). Ja np. jestem zwolennikiem w ogóle nie używania NULL, 0 wydaje mi się czytelniejsze, ale to wszystko kwestia stylu. - Zjarek 2011-08-11 14:08

Pozostało 580 znaków

2011-08-10 23:24
Kumashiro
1
Kumashiro napisał(a)

Można to załatwić jedną pętlą. Nie pilnujesz też zakresów. Jeśli w pliku pole z imieniem i nazwiskiem będzie dłuższe niż 41 znaki, wyjedziesz poza tablicę i zaczniesz pisać po innych zmiennych i/lub dostaniesz SEGFAULT (błąd segmentacji). Pamiętaj, że tablice w C nie pilnują swoich zakresów.

k = i;
while ( !isdigit(t[i]) && (i - k < 41) ) {
tab[x].imienazwisko[i - k] = t[i];
i++;
}
tab[x].imienazwisko[i - k] = '\0';

Echhh... Późno już i nie myślę. W tym przykładzie, który Ci podałem (i zresztą w Twoim kodzie też) jest pewien mały błąd. Ten kod w tab[x].imienazwisko zapisze także spację, która rozdziela imię i nazwisko od kolejnego pola. Aby naprawić to niedopatrzenie, wystarczy w powyższym przykładzie zmienić to:

tab[x].imienazwisko[i - k] = '\0';

na to:

tab[x].imienazwisko[i - k - 1] = '\0';

...chyba. Sorry.

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0