Kreditne kartice

Autor zadatka: Rade Radišić <radisic.rade@uns.ac.rs>

Napisati program koji kontroliše validnost brojeva kreditnih kartica Iz ulazne datoteke učitati u jednostruko spregnutu listu sledeće podatke:

  • ime (jedna reč, do 20 karaktera)

  • prezime (jedna reč, do 30 karaktera)

  • broj_kartice (jedna reč, do 10 karaktera)

Novi elementi liste dodaju se na njen kraj.

Program prima ime ulazne datoteke kao argument komandne linije. Podatke iz ulazne datoteke, potrebno je ispisati u dve različite izlazne datoteke, u zavisnosti od toga da li su kartice validne ili ne. Imena datoteka su validne-kartice.txt i nevalidne-kartice.txt. Prilikom ispisa u izlaznu datoteku, koristiti format "%-8s %-11s %s\n".

Validnost kartice proverava se pomoću Lunovog (Luhn) algoritma na sledeći način:

  • String se obrađuje od poslednjeg elementa ka prvom

  • Svaki karakter se konvertuje u celobrojnu vrednost, a svaki drugi treba pomnožiti sa 2

    • Ukoliko broj koji je pomnožen sa 2 dvocifren, sabiraju se njegove pojedinačne cifre

  • Sve dobijene brojeve treba sumirati, kartica je validna ukoliko je dobijena suma deljiva sa 10

Na standardni izlaz ispisati izveštaj koji sadrži broj validnih, nevalidnih kartica, kao i procenat uspešnosti (odnos validnih u odnosu na ukupan broj kartica izražen u procentima).

U slučaju sledećih grešaka prilikom rada programa, izaći sa odgovarajućim kodom greške:

  • Nedovoljan ili suvišan broj argumenata, kod greške 1 (EXIT_FAILURE)

  • Neuspešno dinamičko zauzeće memorije, kod greške 2

  • Neuspešno otvaranje ulazne datoteke, kod greške 3

  • Neuspešno otvaranje izlazne datoteke za validne kartice, kod greške 4

  • Neuspešno otvaranje izlazne datoteke za nevalidne kartice, kod greške 5

Primer ulazne datoteke kartice.txt:

Joakim   Davidovic   7413468278
Tamara   Zoric       5016398943
Darija   Stefanovic  5220974231
Vedran   Crncevic    7787933709
Djordje  Matijevic   9501431151
Visnja   Gajic       8929550930
Dusica   Kovac       2292961261
Kristina Babic       2025850351
Velimir  Stojanovic  2222179034

Za primer poziva programa:

./a.out kartice.txt

Očekivani ispis na standardnom izlazu je:

Izvestaj

Broj validnih: 7
Sa greskom: 2
Procenat uspesnosti: 77.78%

Sadržaj izlazne datoteke validne-kartice.txt je sledeći:

Joakim   Davidovic   7413468278
Darija   Stefanovic  5220974231
Vedran   Crncevic    7787933709
Visnja   Gajic       8929550930
Dusica   Kovac       2292961261
Kristina Babic       2025850351
Velimir  Stojanovic  2222179034

Sadržaj izlazne datoteke nevalidne-kartice.txt je sledeći:

Tamara   Zoric       5016398943
Djordje  Matijevic   9501431151

Primer rešenja

  1#include <stdio.h>
  2#include <string.h>
  3#include <stdlib.h>
  4
  5#define MAX_IME 21
  6#define MAX_PREZIME 31
  7#define MAX_BROJ_KARTICE 11
  8
  9typedef struct kartica_st {
 10    char ime[MAX_IME];
 11    char prezime[MAX_PREZIME];
 12    char broj_kartice[MAX_BROJ_KARTICE];
 13    struct kartica_st *sledeci;
 14} KARTICA;
 15
 16FILE *safe_fopen(char *, char *, int);
 17void ucitaj_kartice(FILE *, KARTICA **);
 18void ispisi_kartice(FILE *, FILE *, KARTICA *, int *, int *);
 19
 20void inicijalizacija(KARTICA **);
 21KARTICA *napravi_cvor(char *, char *, char *);
 22void dodaj_na_kraj(KARTICA **, KARTICA *);
 23void obrisi_listu(KARTICA **);
 24
 25int provera_broja(char *);
 26
 27int main(int argc, char **argv) {
 28    KARTICA *glava;
 29
 30    if(argc != 2) {
 31        printf("Primer poziva programa: %s kartice.txt\n", argv[0]);
 32        exit(1);
 33    }
 34
 35    inicijalizacija(&glava);
 36
 37    FILE *ulazna = safe_fopen(argv[1], "r", 3);
 38    ucitaj_kartice(ulazna, &glava);
 39    fclose(ulazna);
 40
 41    FILE *izlazna_validni = safe_fopen("validne-kartice.txt", "w", 4);
 42    FILE *izlazna_nevalidni = safe_fopen("nevalidne-kartice.txt", "w", 5);
 43    int broj_validnih = 0, broj_nevalidnih = 0;
 44    ispisi_kartice(izlazna_validni, izlazna_nevalidni, glava, &broj_validnih, &broj_nevalidnih);
 45    printf("Izvestaj\n\n");
 46    printf("Broj validnih: %d\n", broj_validnih);
 47    printf("Sa greskom: %d\n", broj_nevalidnih);
 48    printf("Procenat uspesnosti: %.2lf%%\n", (double)broj_validnih / (broj_validnih + broj_nevalidnih) * 100);
 49    fclose(izlazna_validni);
 50    fclose(izlazna_nevalidni);
 51
 52    obrisi_listu(&glava);
 53
 54    return 0;
 55}
 56
 57FILE *safe_fopen(char *ime, char *rezim, int kod_greske) {
 58    FILE *fp = fopen(ime, rezim);
 59
 60    if(fp == NULL) {
 61        printf("Greska prilikom otvaranja %s datoteke!\n", ime);
 62        exit(kod_greske);
 63    }
 64
 65    return fp;
 66}
 67
 68void ucitaj_kartice(FILE *pulazna, KARTICA **pglava) {
 69    char tmp_ime[MAX_IME];
 70    char tmp_prezime[MAX_PREZIME];
 71    char tmp_broj_kartice[MAX_BROJ_KARTICE];
 72
 73    while(fscanf(pulazna, "%s %s %s",
 74                 tmp_ime,
 75                 tmp_prezime,
 76                 tmp_broj_kartice) != EOF) {
 77        KARTICA *novi = napravi_cvor(
 78            tmp_ime,
 79            tmp_prezime,
 80            tmp_broj_kartice);
 81        dodaj_na_kraj(pglava, novi);
 82    }
 83
 84}
 85
 86void ispisi_kartice(FILE *izlazna_validni, FILE *izlazna_nevalidni, KARTICA *glava,
 87                    int *pbroj_validnih, int *pbroj_nevalidnih) {
 88    KARTICA *tekuci = glava;
 89    FILE *izlazna;
 90    int validan_broj;
 91
 92    while(tekuci != NULL) {
 93        if(provera_broja(tekuci->broj_kartice)) {
 94            izlazna = izlazna_validni;
 95            (*pbroj_validnih)++;
 96        } else {
 97            izlazna = izlazna_nevalidni;
 98            (*pbroj_nevalidnih)++;
 99        }
100
101        fprintf(izlazna, "%-8s %-11s %s\n", tekuci->ime, tekuci->prezime, tekuci->broj_kartice);
102
103        tekuci = tekuci->sledeci;
104    }
105}
106
107void inicijalizacija(KARTICA **pglava) {
108    *pglava = NULL;
109}
110
111KARTICA *napravi_cvor(char *ime, char *prezime, char *broj_kartice) {
112    KARTICA *novi = (KARTICA *)malloc(sizeof(KARTICA));
113
114    if(novi == NULL) {
115        printf("Greska prilikom zauzimanja memorije!\n");
116        exit(2);
117    }
118
119    strcpy(novi->ime, ime);
120    strcpy(novi->prezime, prezime);
121    strcpy(novi->broj_kartice, broj_kartice);
122    novi->sledeci = NULL;
123
124    return novi;
125}
126
127void dodaj_na_kraj(KARTICA **pglava, KARTICA *novi) {
128    if(*pglava == NULL) {
129        *pglava = novi;
130    } else {
131        KARTICA *tekuci = *pglava;
132
133        while(tekuci->sledeci != NULL) {
134            tekuci = tekuci->sledeci;
135        }
136
137        tekuci->sledeci = novi;
138    }
139}
140
141void obrisi_listu(KARTICA **pglava) {
142    KARTICA *tmp;
143
144    while(*pglava != NULL) {
145        tmp = *pglava;
146        *pglava = (*pglava)->sledeci;
147        tmp->sledeci = NULL;
148        free(tmp);
149    }
150}
151
152int provera_broja(char *broj_kartice) {
153    int i, duzina = strlen(broj_kartice), suma = 0, je_drugi = 0;
154
155    for(i = duzina - 1;i >= 0;i--) {
156        int broj = broj_kartice[i] - '0';
157
158        if(je_drugi) {
159            broj *= 2;
160        }
161
162        // ako je broj dvocifren, ovo ce pomoci da se dobiju sume njegovih cifara (npr. 18 -> 1 + 8)
163        // ako je jednocifren, broj / 10 ce biti 0, a broj % 10 ce biti sam broj
164        suma += broj / 10;
165        suma += broj % 10;
166
167        je_drugi = !je_drugi;
168    }
169
170    return suma % 10 == 0;
171}