1. Homepage of Dr. Zoltán Porkoláb
    1. Home
    2. Archive
  2. Teaching
    1. Timetable
    2. Bolyai College
    3. C++ (for mathematicians)
    4. Imperative programming (BSc)
    5. Multiparadigm programming (MSc)
    6. Programming (MSc Aut. Sys.)
    7. Programming languages (PhD)
    8. Software technology lab
    9. Theses proposals (BSc and MSc)
  3. Research
    1. Sustrainability
    2. CodeChecker
    3. CodeCompass
    4. Templight
    5. Projects
    6. Conferences
    7. Publications
    8. PhD students
  4. Affiliations
    1. Dept. of Programming Languages and Compilers
    2. Ericsson Hungary Ltd

Imperatív programozás 12.

A string- és filekezelő könyvtár használata

Ez alkalommal egy telefonkönyv programot írunk be. Segítségével bemutatjuk a stringkezelés és az alapvető fájlkezelés függvényeit.

Ez mostani 2022-es implementáció, lejjebb a 2019-es megvalósítást lehet megtalálni, ami eléggé eltér a 2022-estől.

Az előadás videója.

Implementáció diamikusan növekvő memóriával (2022)

A 2022-as implementáció egy dinamikusan allokált tömböt tartalmaz, ahol elhelyezzük az egyes bejegyzéseket. A bejegyzések (név,telefonszám) pointer párok, magukat a neveket és telefonszámokat dinamikusan allokáljuk, a phonebook tömbben csak a pointer-párok vannak. A bejegyzéseket a nevek szerint növekvő sorrendben tároljuk el.

Beszúráskor megkeressük azt a helyet, ahova az ábécé sorrend szerint el kell helyeznünk az új bejegyzést, a hátrább levő bejegyzéseket egy hellyel hátrébb csúsztatva helyet csinálunk az új bejegyzésnek, és a keletkezett helyre rakjuk az új elemet. Szükség esetén megnöveljük a dinamikus tömböt.

Törlésnél a törlendő elem által mutatott név és telefonszám területeket felszabadítjuk és a mögöttes elemeket feltoljuk a keletkezett “lyuk” helyére.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
/*
 * Egy egyszerű telefonkönyv implementációja 
 *
 * ELsősorban a parancssor használata, iletve a standard
 * C könyvtár stringkezelő függvényeinek beutatására.
 *
 */

#include <stdio.h>    /* szabványos I/O */
#include <string.h>   /* string manipulációk */
#include <stdlib.h>   /* exit, malloc */

#define BUFSIZE  80   /* beolvasott parancs max hossz */
#define INITSIZE 4    /* telefonkönyv kezdeti kapacitás */
#define MAXARGS  2    /* maximum paraméterszám a parancsokhoz */
#define PROMPT   "PB> "  /* a kiírt prompt */
#define DEFAULT_FILE "phonebook.txt"  /* default fájlnév */

/* a parancsokat előzetesen deklaráljuk, hogy lehessen 
 * használni a rájuk mutató függvény-pointereket
 */
void add( char *name, char *phone);
void del( char *name);
void print(char *name);
void list(void);
void clear(void);
void save(void);
void saveAs(char *name);
void load(void);
void loadFrom( char *name);
void help(void);
void quit(void);


typedef void (*comm)();  /* a függvény-pointer típusa */

typedef struct COMMAND /* ezt tudjuk egy parancsról */
{
  char *name; /* a parancs neve */
  int   npar; /* paraméterszáma */ 
  comm  func; /* a végrehajtandó függvény */
}
command_t;

command_t commands[] = {  /* az implementált parancsok */
  { "add",      2, add      },
  { "del",      1, del      },
  { "print",    1, print    },
  { "list",     0, list     },
  { "clear",    0, clear    },
  { "save",     0, save     },
  { "saveAs",   1, saveAs   },
  { "load",     0, load     },
  { "loadFrom", 1, loadFrom },
  { "quit",     0, quit     },
  { "help",     0, help     }
};
const int nCommands = sizeof(commands)/sizeof(commands[0]);

typedef struct ENTRY  /* egy bejegyzés a telefonkönyvben */
{
  char *name;  /* név */
  char *phone; /* telefonszám */
}
entry_t;

static int capacity = 0;  /* a bejegyzések buffermérete */
static int size     = 0;  /* a bejegyzések száma */
static entry_t *pb  = NULL; /* pointer a bejegyzésekre */

int  findCommand(char *buffer);
int  findParams( char *buffer, char **par1, char **par2);
void grow(void);

int main()
{
  char  buffer[BUFSIZE]; /* a felhasználó inputja */
  char *args[MAXARGS];   /* ezek mutatnak a paraméterekre */

  /* kiírjuk a promptot, majd olvassuk a user inputot */
  while ( printf(PROMPT), fgets( buffer, BUFSIZE, stdin) )
  {
    int i;
    if ( -1 != (i = findCommand(buffer)) )
    {
       /* megtaláltuk a parancsot, keressük a paramétereket */ 
       int npars = findParams(buffer, &args[0], &args[1]);	    
       if ( npars != commands[i].npar )
       {
          /* hibás paraméterszám */	       
          fprintf( stderr, "command %s requires %d parameters\n", 
			       commands[i].name, commands[i].npar);	       
	  continue; /* ismét olvasni fogunk egy parancsot */
       }
       /* van parancs, vannak paraméterek */
       switch( commands[i].npar )
       {
       case 0:  commands[i].func();                 break; 
       case 1:  commands[i].func(args[0]);          break;
       case 2:  commands[i].func(args[0], args[1]); break;	       
       default: fprintf(stderr, "max %d parameters are supported\n", 
                                     MAXARGS); 
		break;
       }
    }	    
  }
  quit();   /* EOF esetén quit akció */
  return 0;
}

/************** helper functions ***************/

/* 
 * Megkeresi a felhasználó első szavát a parancsok közt.
 * Ha talál, visszatér a parancs indexével, különben -1-el
 */
int findCommand(char *buffer)
{
  char command[BUFSIZE];

  if ( 1 == sscanf( buffer, "%s", command) ) /* első szó */
  {
    for ( int i = 0; i < nCommands; ++i)
    {
      if ( 0 == strcmp( commands[i].name, command) )
      { 
        return i; /* megtaláltuk */
      }
    }	 
    fprintf( stderr, "Unknown command: %s\n", command);
  }   
  return -1;  /* nincs ilyen parancs */
}

/* 
 * Megkeresi a paramétereket és beállítja *par1, *par2-be,
 * vagy NULL-t rak, ha nem volt paraméter.
 * Viszatér a megtalált paraméterek számával.
 */
int findParams(char *buffer, char **par1, char **par2)
{
  strtok( buffer, " \n\t");  /* átlépi az első szót */
  
  *par1 = strtok( NULL, " \n\t"); /* második szó */
  *par2 = strtok( NULL, "\n\t");  /* sor végéig, tart.hat space-t */

  return (NULL != *par1)+(NULL != *par2); /* hány paraméter volt */
}

/*
 * Megkeresi a ``name'' bejegyzést, 
 * és visszatér az indexével vagy -1-el.
 */
int find(char *name) 
{
  for (int i = 0; i < size; ++i)
  {
    if ( 0 == strcmp( pb[i].name, name) )	  
    {
      return i;	   /* megtalálta */
    }
  }
  return -1;  /* nem találta meg */
}

/*
 * Elvégzi a bejegyzések buffere keezdeti inicializálását,
 * illetve megduplázza a buffer méretét. 
 */ 
void grow(void)
{
  if ( 0 == capacity )  /* kezdeti inicilizálás */
  {
    pb = (entry_t*)malloc( INITSIZE*sizeof(entry_t) );	  
    if ( NULL == pb )
    {
      fprintf(stderr,"Out of memory\n");
      exit(1);      
    }
    capacity = INITSIZE;
  }  
  else /* kapacitás megnövelése */
  {
    entry_t *ptr = (entry_t*)realloc(pb,2*capacity*sizeof(entry_t));  
    if ( NULL == ptr )
    {
      fprintf(stderr,"Out of memory\n");
      exit(1);
    }
    pb = ptr;
    capacity *= 2;
  }
}

/*************** actions ********************/

void quit(void)
{
  printf("quiting...\n"); 	
  clear();
  free(pb);
  exit(0);	
}

void help(void)
{
  for ( int i = 0; i < nCommands; ++i)
  {
    int   pars = commands[i].npar;
    char *name = commands[i].name;
    printf("%s%s%s\n", name, pars>0?" name":"", pars>1?" phone":"");	
  }
}

void list(void)
{
  for ( int i = 0; i < size; ++i)
  {
    printf("%s: %s\n", pb[i].name, pb[i].phone);
  }
}

void add(char *name, char *phone)
{
  int i, behind;
  entry_t newEntry;

  /* helyet foglalunk a névnek és a telefonszámnak */
  newEntry.name  = (char *) malloc(sizeof(name)+1);
  newEntry.phone = (char *) malloc(sizeof(phone)+1);  
  if ( NULL == newEntry.name || NULL == newEntry.phone )
  {
    fprintf(stderr,"Out of memory\n");
    exit(1);
  }
  /* bemásoljuk a nevet és telefonszámot */
  strcpy( newEntry.name, name);
  strcpy( newEntry.phone, phone);  

  if ( size == capacity ) /* ha nem lenne hely az entry bufferben */
  {
    grow();	  
  }

  /* keressük azt a pozíciót, amely először nagyobb a
   * beszúrandó névnél, ha nincs ilyen, akkor a 
   * dinamikus tömb végére pozícionálunk 
   */  
  i = 0;
  while ( i < size && 0 >= strcmp(pb[i].name, name) )
  {
    ++i;
  }
  /* ok, az i-ik pozícióra akarunk beszúrni */

  /* hátratoljuk az [i..size-1] elemeket eggyel, 
   * hogy helyet csináljunk a beszúrandó új elemnek 
   */
  behind = size - i; /* ennyi elem van a végéig */
  memmove( &pb[i+1], &pb[i], behind*sizeof(entry_t));

  /* beírjuk az új elemet */
  pb[i] = newEntry;
  ++size;
}

void print(char *name)
{
  int i = find(name);  	
  if ( -1 == i )
  {
    fprintf(stderr, "print: entry %s is not found\n", name);	  
  }  
  else
  {
    printf("%s: %s\n", pb[i].name, pb[i].phone);	  
  }
}

void del(char *name)
{
  int i = find(name);
  if ( -1 == i )
  {
    fprintf(stderr, "del: entry %s is not found\n", name);	  
  }  
  else
  {
    /* előrecsúsztatjuk az [i+1..size-1] elemeket,
     * hogy ne legyen lyuk a törölt elem helyén
     */
    int behind = size-i-1; /* ennyi elem van a végéig */	  
    free(pb[i].name);
    free(pb[i].phone);    
    memmove(&pb[i], &pb[i+1], behind*sizeof(entry_t));	  
    --size;
  }
}

void clear(void)
{
  for ( int i = 0; i < size; ++i) 	
  {
    free( pb[i].name);
    free( pb[i].phone);    
  }
  size = 0;
}

void saveAs( char *name)
{
  FILE *fp = fopen( name, "w");
  if ( NULL == fp )
  {
    fprintf( stderr, "Can't open %s for write\n", name);
  }
  else
  {
    int i;
    for ( i = 0; i < size; ++i )
    {
      fprintf( fp, "%s:%s\n", pb[i].name, 
                              pb[i].phone);	    
    }
    fclose(fp);
  }
}  	

void save(void)
{
  saveAs(DEFAULT_FILE);
}

/* töröljük a kurrens elemeket, beolvasunk egy fájlt és 
 *  hozzáadjuk ez elemeit az üres telefonkönyvhöz 
 */
void loadFrom( char *name)
{
  FILE *fp = fopen( name, "r");
  if ( NULL == fp )
  {
    fprintf( stderr, "Can't open %s for read\n", name);
  }
  else
  {
    char buffer[BUFSIZE];
    clear(); /* töröljük a meglevő elemeket */
    while ( fgets(buffer, BUFSIZE, fp) ) /* a fájl sorai */
    {
      /* kéne legyen benne egy ':' karakter */
      char *ptr = strchr(buffer, ':');
      if ( ptr )  /* megtaláltuk */
      {
        char *name   = buffer;  /* a név a ':' előtt  */
	*ptr = '\0';            /* termináljuk a nevet */
        char *number = ptr+1;	/* a szám a ':' után  */      
	ptr = strchr( number, '\n'); 
	if ( ptr ) 
          *ptr = '\0'; /* felülírjuk a sorvégi '\n'-t */		
	add( name, number);  /* hozzáadjuk a könyvhöz */
      }      
      else
      {
        fprintf( fp, "Bad file format %s\n", buffer);
	break;
        /* break; nem akarunk továbbmenni hiba esetén */
      }
    }
    fclose(fp);
  }
}

void load(void)
{
  loadFrom(DEFAULT_FILE);	
}

Implementáció láncolt listával (2019)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define INPUTSIZE  80 /* max input line */
#define DEFAULT_NAME "phonebook.txt" /* default filenév */

/* lista elemek a telefonkönyv bejegyzésekre */
typedef struct ELEM
{
  char        *name; 
  char        *phone;
  struct ELEM *next;  
  struct ELEM *prev;
}
elem_t;

/* maga a telefonkönyv típusa */
typedef struct LIST
{
  elem_t *first;
  elem_t *last;
  int     size;  
}
list_t;

/* a telefonkönyv */
list_t phoneBook = { NULL, NULL, 0};

/* az egyes parancsok fv-re mutató pointer típusa */
typedef int (*comfunc_t)(); 

/* az egyes parancsok */
typedef struct COMMAND
{
  char     *comStr;  /* neve */
  int       nPars;   /* paraméterszáma */
  comfunc_t comFunc; /* a fv pointer */ 
} 
command_t;

/* a parancsok deklarációja */
int help(void);
int quit(void);
int list(void);
int add( char *name, char *phone);
int del( char *name);
int print( char *name);
int save(void);
int saveas( char *fname);
int load(void);
int loadfrom( char *fname);

/* segédfüggvények */
command_t *findCommand( char *buffer);
int findParams( command_t *comPtr, char **pars);
int insert( elem_t *before, char *name, char *phone);
void erase( elem_t *ptr);

/* a parancsok */
static command_t commands[] = {
  { "help",  0, help  },
  { "quit",  0, quit  },
  { "list",  0, list  },
  { "add",   2, add   },
  { "del",   1, del   },
  { "print", 1, print }, 
  { "save",  0, save  },
  { "saveas",1, saveas},
  { "load",  0, load  },
  { "loadfrom", 1, loadfrom }  
};

/* a parancsok száma */
const int nCommands = sizeof(commands)/sizeof(commands[0]);

int main()
{
  char buffer[INPUTSIZE];

  printf("PB> ");
  while ( NULL != fgets( buffer, INPUTSIZE, stdin) )
  {
    command_t *comPtr;	  
    if ( (comPtr = findCommand( buffer)) )	  
    {
      char *pars[2] = { NULL, NULL};	  
      if( findParams( comPtr, pars) )
      {
        switch(comPtr->nPars)
        {
        case 0: comPtr->comFunc();                  break;
        case 1: comPtr->comFunc(pars[0]);           break;		
        case 2: comPtr->comFunc(pars[0], pars[1]);  break;		
        default: /* more par not implemented yet */ break;
        }					  		  
      }	
    }    
    printf("PB> ");
  }
  return 0;
}

command_t *findCommand( char *buffer)
{
  int   i; 
  char *beg = strtok( buffer, " \t\n");

  if ( NULL == beg )    
     return 0; 

  for ( i = 0; i < nCommands; ++i)
  {
    if ( 0 == strcmp( commands[i].comStr, beg) )
    {
      return &commands[i];      
    }	    
  }	  
  fprintf( stderr, "Unknown command: %s\n", buffer);
  return 0;
}

int findParams( command_t *comPtr, char **params)
{
  char  *par1, *par2;	
  if ( 0 == comPtr->nPars )
  {	  
    return 1;	   
  }
  else if ( NULL == (par1 = strtok( NULL, " \t\n")) )
  {
    fprintf( stderr, "Too few parameters: %s NAME\n", 
             comPtr->comStr);
    return 0;     
  }
  else if ( 1 == comPtr->nPars )
  {	   
    params[0] = par1;
    return 1;
  }
  else if ( NULL == (par2 = strtok( NULL, "\t\n")) ) 
  {
    /* space not here, so phone number can include space */
    fprintf( stderr, "Too few parameters: %s NAME PHONE\n", 
             comPtr->comStr);
    return 0;     
  }
  else if ( 2 == comPtr->nPars )
  {
    params[0] = par1;
    params[1] = par2;
    return 1;       
  }	   
  else
  {
    /* more parameters are not implemented yet */    
    return 0;
  }    
}

int help(void)
{
  int i = 0;
  printf( "Usage: command par1 par2\n  commands: ");  
  for ( i = 0; i < nCommands; ++i)
  {
    printf( "%s, ", commands[i].comStr);  
  }	
  printf("\n");
  return 1;
}

int quit(void)
{
  exit(0);
}

int list(void)
{
  elem_t *ptr = phoneBook.first;
  printf("======= entries =======\n");
  while ( ptr )
  {
    printf( "%s:\t%s\n", ptr->name, ptr->phone);
    ptr = ptr->next;    
  }
  printf("=======================\n");
  return 1;
}

int saveas(char *fname)
{
  elem_t *ptr;	
  FILE *fp = fopen( fname, "w"); 
  if ( NULL == fp )
  {
    fprintf( stderr, "Can open %s for write\n", fname);	  
    return 0;
  }
  ptr = phoneBook.first;
  while ( ptr )
  {
    fprintf( fp, "%s:%s\n", ptr->name, ptr->phone);
    ptr = ptr->next;    
  }
  fclose(fp);
  return 1;
}

int save(void)
{
  return saveas( DEFAULT_NAME); 
}

int load(void)
{
  return loadfrom( DEFAULT_NAME);  
}

int loadfrom( char *fname)
{
  char *name;
  char *phone;
  char  buffer[INPUTSIZE];
  FILE *fp = fopen( fname, "r"); 

  if ( NULL == fp )
  {	  
    fprintf( stderr, "Can open %s for read\n", fname);	  
    return 0;
  }
  while ( NULL != fgets( buffer, INPUTSIZE, fp) )
  { 
    if ( NULL == strchr( buffer, ':') )
    {
      fprintf( stderr, "Bad file format: %s\n", buffer);
      return 1;      
    }
    name = strtok( buffer, ":");
    phone = strtok( NULL, "\n");
    add( name, phone);
  }
  return 1;
}

int add(char *name, char*phone)
{
  elem_t *ptr = phoneBook.first;

  while ( ptr )
  {
    if ( 0 == strcmp( ptr->name, name) )
    {	    
      printf( "add: %s already in phonebook\n", name);
      return 0;
    }
    else if ( 0 > strcmp( ptr->name, name) )
    {	    
      ptr = ptr->next;    
    }
    else 
    {
      /* insert before */
      return insert( ptr->prev, name, phone); 
    }	    
  }
  /* insert last */	 
  return insert( phoneBook.last, name, phone); 
}

int insert( elem_t *before, char *name, char *phone)
{      	
  elem_t *newPtr = (elem_t*) malloc(sizeof(elem_t));	    
  if ( NULL == newPtr )
  {
    fprintf(stderr, "add: no memory\n");
    return 0;    
  }
  if ( 0 == phoneBook.size )
  {
    newPtr->next = newPtr->prev = NULL;
    phoneBook.first = phoneBook.last = newPtr;    
  }	  
  else 
  {	  
    newPtr->prev = before;
    newPtr->next = before ? before->next : phoneBook.first;           
    if ( newPtr->prev ) newPtr->prev->next = newPtr; 
    else phoneBook.first = newPtr;
    if ( newPtr->next ) newPtr->next->prev = newPtr; 
    else phoneBook.last = newPtr;      
  }
  ++phoneBook.size;  
  newPtr->name  = (char *) malloc( strlen(name)+1); 
  newPtr->phone = (char *) malloc( strlen(phone)+1); 
  strcpy( newPtr->name,  name);	  
  strcpy( newPtr->phone, phone);
  return 1;
}

void erase( elem_t *ptr)
{
  if ( ptr->prev )
    ptr->prev->next = ptr->next;
  if ( ptr->next ) 
    ptr->next->prev = ptr->prev;
  if ( phoneBook.first == ptr )
    phoneBook.first = ptr->next;  
  if ( phoneBook.last  == ptr ) 
    phoneBook.last  = ptr->prev;  
  --phoneBook.size;
  free(ptr);
}

int del(char *name)
{
  elem_t *ptr = phoneBook.first;

  while ( ptr )
  {
    if ( 0 == strcmp( ptr->name, name) )
    {	    
      free(ptr->name);
      free(ptr->phone);
      erase(ptr);
      return 1;
    }
    ptr = ptr->next;
  }
  printf("del: %s not found\n", name);
  return 1;
}

int print(char *name)
{
  elem_t *ptr = phoneBook.first;

  while ( ptr )
  {
    if ( 0 == strcmp( ptr->name, name) )
    {	    
      printf( "%s:\t%s\n", ptr->name, ptr->phone);
      return 1;
    }
    ptr = ptr->next;
  }
  printf("print: %s not found\n", name);
  return 0;
}