Thread: Help with phone book console app in C

  1. #1
    Registered User
    Join Date
    Dec 2009
    Posts
    23

    Help with phone book console app in C

    Hey guys,

    I'm sorry for the long post, but I've been working on this for 2 days and I still can't figure it out.

    I made a phone book console app just to sharpen up on the basics of C. Thus far, I've added an add and look up feature.

    The add feature I have right now will only add a single name (no last name) and a number. The add feature of my phone book app prints the added names and numbers into a text file in the following format:

    name number
    name2 number2
    ect. ect.

    I would like to be able to read in a full name and a last name, but I am having trouble implementing my idea into syntax because intermixing scanf and fgets for keyboard input gives me strange logical errors. These errors include my names and numbers being copied in 2-3 spaces below the other names in the text file, or sometimes they won't be copied at all.

    Here is my code before I add any fgets() function to the add function:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    char name[25];
    int number[25];
    lookup();
    add();
    add2();
    dump_line();
    noname();
    
    main()
    {
     int x=0;
    
    	
     
    	printf("       *********************PHONE BOOK********************\n\n");
    		printf("1. Lookup\n2. Add new entry\n3. Exit\n\n");
    			
    			scanf("%d",&x);
    
    			while (x != 1 && x != 2 && x != 3){
    				dump_line(stdin);
    				scanf("%d",&x);
    			}
    
    			if (x == 1 )
    			{
    				system("cls");
    				lookup();
    			}
    
    					else if (x ==2)
    					{
    						system("cls");
    						add();
    					}
    					else{
    						printf("Exiting phonebook\n");
    							return 0;
    					}
    
    			getch();
    
    } /********END OF MAIN FUNCTION***********/
    
    lookup()
    {	
    	int x,y=5;
     FILE *pt;
     char findname[25];
    char words[25];
    	
    
    	
    pt = fopen ("phonebook.txt","r");
    	if (pt == NULL)
    	{
    		printf("File not found!\n");
    			return 0;
    	}
    
    	printf("        *****************PHONE BOOK LOOKUP*****************\n\n\n");
    		printf("Type \"all\" to list all entries\n\n");
    	printf("Enter a name: ");
    		
    				scanf("%25s",name);
    					
    		if ( stricmp(name,"all") != 0 ){
    				do{	
    					if (y == EOF){
    						printf("Name doesn't exist\n\n");
    							fclose(pt);
    							getch();
    							noname();
    				}					
    					y = fscanf(pt,"%s",findname);
    						
    				}while (stricmp(name,findname) != 0);
    						
    					fgets (number,25,pt);
    						printf("\n\nName: %s\nNumber:%s",findname,number);
    						
    		}
    
    		else if ( stricmp (name,"all") == 0 )
    		{
    			x = 1;
    				printf("\n");
    				do {
    				x = fscanf(pt,"%s",words);
    					if (x == EOF)
    						break;
    			printf("Name: %s\n",words);
    				x = fscanf(pt,"%s",words);
    			printf("Number: %s\n",words);
    					printf("\n---------------------------\n");
    				} while (x != EOF);
    				
    		}
    				fclose(pt);
    			getch();
    
    			printf("\n\nWould you like to look up another name? (y//n)\n\n");
    
    			do {
    				x = getch();
    			} while (x != 'y' && x != 'n');
    
    			if (x == 'y'){
    				system("cls");
    					lookup();
    			}
    
    			else if (x == 'n'){
    				system("cls");
    					main();
    			}
    	fclose (pt);
    						
    } /*******END OF LOOKUP FUNCTION************/
    
    add()
    {
    	FILE *pt;
    		pt = fopen ("phonebook.txt","a");
    		printf("            *****************ADD ENTRY*****************\n\n");
    			printf("Add in this format, e.g. joe 555-555-5555\n\n\n");
    	printf("Add a name and a number now: \n\n");
    			
    			if (pt == NULL)
    			{
    				printf("NO PHONEBOOK FOUND!\n");
    					return 0;
    
    			}
    					scanf("%s %s",name,number);
    				fprintf (pt,"%s %s\n",name,number);
    
    			printf("\nNumber added successfully!\n\n");
    				fclose(pt);
    			getch();
    				add2();
    		
    } /*******END OF ADD FUNCTION********/
    
    
    add2()
    {
    	int option;
    	
    	
    	printf("Would you like to add another number? (y//n)\n");
    	do {
    		option = getch();
    	  } while (option != 'y' && option != 'n');
    
    	if (option == 'y'){
    			system ("cls");
    		add();
    	}
    	
    		else {
    		  printf("\nReturning to main menu\n\n");
    			system ("cls");
    				main();
    		}
    			
    } /***end of add2 function***/
    
    noname()
    {
    	int option;
    printf("Would you like to look up another name? (y//n)\n\n");
    	
    	do{
    	option = getch();
    	} while (option != 'y' && option != 'n');
    
    			if (option == 'y'){
    				system("cls");
    				add();
    			}
    
    
    		else {
    				printf("Returning to main menu\n\n");
    					system ("cls");
    					main();
    		}
    } /***end of noname function****/
    
    
    dump_line(FILE *fp)
    {
      int ch;
    
      while( (ch = fgetc(fp)) != EOF && ch != '\n' )
        /* null body */;
    }
    This code works fine. It's only missing the ability to read in a full name and phone number.

    As far as reading in a full line goes, I've tried to use a predefined function (dump_line in my code) to clear the input buffer after the first scanf in my main function, so that I could use an fgets in my add function to get a full line of text, which can then be added to the text file. It just keeps giving me the same kind of logical errors that I've described above.

    Actually, fgets seems to have unpredictable results even when used to read from the keyboard instead of a file (ie. (line_of_char,buff size,stdin) ).

    I've been trying to figure out how to do this for 2 days, with no avail.

    To reiterate my main problem and add a second question to my post:

    1.) How would I accomplish being able to input a full name and a number into the text file, without requiring an fgets function to read in a full line?

    2.) How would I make it so that the names are stored in the text file alphabetically, so that when they are all listed in my lookup function, they will be in alphabetical order. I understand that I can use stricmp, but how do I manipulate a text file so that I can insert names and space down other names when needed? Is this possible?

    Btw, feel free to constructively criticize my code in any way. I'm a noob for now, but I'm learning.

    Again, I'm sorry for the long post.

    Thanks guys.

    ~Billy
    Last edited by cb0ardpr0gr^mm3r; 11-11-2010 at 02:36 AM.

  2. #2
    Banned
    Join Date
    Aug 2010
    Location
    Ontario Canada
    Posts
    9,547
    Instead of simple arrays, you should investigate using a linked list of structs... or perhaps a binary disk file of structs, with disk file searching instead of memory.

    Code:
    typedef struct tPBOOK
      {  char fname[24];
         char lname[24];
         char city[24];
         char number[16];
         void *prev;
         void *next; }
       PBOOK, *pPBOOK;

  3. #3
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    First, some general comments:

    Indentations in lines of code, aren't just for looks, they show which lines are subordinate logically, and by how much. Keep them neatly and consistently 2-5 spaces (pick a number), and your eye will catch a LOT of logic bugs, SO EASILY. So will we.

    Yours looks like Raggedy Anne went on a left margin drunken tear, OK? Lordy!

    Now, onto the code:

    Use scanf() sparingly - and add a getchar() after each one, to remove the trailing newline char, that scanf() always leaves behind (caused by you hitting the enter key). That will clear out a number of errors, from scanf() and fgets(). There is NOTHING wrong with fgets(), and you should use it more frequently. Never:

    scanf("%s %s %d %d %s", fname, lname, &stNum, stName);

    The users will ALWAYS screw that kind of input, into the middle of next week!

    Instead, use fgets(), get the whole line of input, and then use:
    sscanf(buffer, "%s %s %d %d %s", etc.) //like scanf(), but now YOU have the data, and you can run any tests on it, you want, as many times as you want, to help validate the format and content of it.

    This will be helpful - removes the newline from the buffer, when you use fgets():

    Code:
    if(buffer[strlen(buffer)-1]=='\n')
       buffer[strlen(buffer)-1]='\0';  //removes newline from buffer
    gets() is perfect for this EXCEPT it's so unsafe you should never use it. Use fgets() instead, and use the above to make it "clean". Another good thing about fgets() is that it doesn't leave the newline char behind (it includes it obviously). So no getchar()'s are needed to "pull" the old newlines, off the keyboard buffer.

    This isn't a finished program, but look at how this program is done:

    Code:
    /* 
    */ 
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAXTITLE 70
    #define MAXACTOR 70
    #define MAXVIDS 10
    
    struct video {
      char title[MAXTITLE];
      char actor[MAXACTOR];
      int  rating;
    };
    typedef struct video vid;
    int Size = sizeof(vid);
    
    int addVideos(vid *vids);    //function proto's
    int idelete(vid *vids);
    int edit(vid *vids);
    int fetch(vid *vids, char *buff);
    void menu(vid *vids, int vidNum);
    void sortRecords(vid *vids);
    void viewAll(vid *vids);
    int writeAll(vid *vids, int vidNum);
    
    int main(void)
    {
      vid vids[MAXVIDS];
      char buff[80];
      char ch;
      int i;
      int index, filecount, vidNum=0;
      FILE * fp_in, *fp_out;
      
      //gets rid of the junk from vids[] records
      for (index = 0; index < MAXVIDS; index++) {   // this is to 
    
    make all empty files equal to 'deleted' status
        vids[index].title[0] = '\0';              // thus searchable 
    
    in an 'if' statement
        strcpy(vids[index].actor, "");
        vids[index].rating = 0;
      } 
      if((fp_in = fopen("videodb.txt", "r")) == NULL) {  
        fputs("Can't open videodb.txt\n", stderr);
        exit(EXIT_FAILURE);
      }
      i=0;
      while((fgets(buff, sizeof(buff), fp_in))!=NULL) {
        buff[strlen(buff)-1]='\0';
        strcpy(vids[i].title, buff);
        vidNum++;
        fgets(buff, sizeof(buff), fp_in);
        buff[strlen(buff)-1]='\0';
        strcpy(vids[i].actor, buff);
        fgets(buff, sizeof(buff), fp_in);
        buff[strlen(buff)-1]='\0';
        sscanf(buff, "%d", &vids[i].rating);
        if(++i==MAXVIDS) 
          break;
      }
    
      fclose(fp_in);
      menu(vids, vidNum);
    
      return 0;
    }
    int addVideos(vid *vids) {
      int i,j,ok;
      char buff[80];
      char ch='y';
      vid vid1;
    
      //for(i=0;i<4;i++) {
      while(ch=='y' || ch=='Y') {
        printf("\nEnter the vid's title: ");
        fgets(buff, MAXTITLE, stdin);
        buff[strlen(buff)-1]='\0';
        strcpy(vid1.title, buff);
    
        printf("Enter the vid's principal actor: ");
        fgets(buff, MAXACTOR, stdin);
        buff[strlen(buff)-1]='\0';
        strcpy(vid1.actor, buff);
    
        printf("\nEnter the value: ");
        fgets(buff, sizeof(vids[0].rating), stdin);
        sscanf(buff, "%d", &vid1.rating);
        
        for(j=0,ok=0;j<MAXVIDS;j++) {
          if(vids[j].title[0]=='\0') {
            strcpy(vids[j].title, vid1.title);
            strcpy(vids[j].actor, vid1.actor);
            vids[j].rating = vid1.rating;
            ok=1;
            break;
          }
        }
        if(!ok) {
          printf("\nArray is full, couldn't add that record");
          return 1;   //add failed
        }
        printf("\nAdd another vid? [y/n]: ");
        scanf("%1c", &ch);
        (void) getchar();
      }
      return 0;       //successful add
    }
    int edit(vid *vids) {
      char buff[80];
      int i=0;
    
      printf("\nEnter the title of the video you want to edit: ");
      fgets(buff, MAXTITLE, stdin); 
      if(buff[strlen(buff)-1]=='\n') 
        buff[strlen(buff)-1]='\0';
    
      i = fetch(vids, buff); //in the list?
      if(!i)                 //nope  
        return 0;
    
      //video was found, i is it's index in the array
      printf("\n\t  Title: %s\n\t  Actor: %s\n\t rating: %2d\n",
      vids[i].title, vids[i].actor, vids[i].rating);
      printf("\n\t  Enter new title [hit <enter> to leave it 
    
    unchanged]: ");
      fgets(buff, MAXTITLE, stdin);
      if(strlen(buff) >1) { 
        if(buff[strlen(buff)-1]=='\n') {
          buff[strlen(buff)-1]='\0';
          strcpy(vids[i].title, buff);
        }
      }
      printf("\n\t  Enter new actor [<enter> to leave it unchanged]: 
    
    ");
      fgets(buff, MAXACTOR, stdin);
      if(strlen(buff) >1) { 
        if(buff[strlen(buff)-1]=='\n') {
          buff[strlen(buff)-1]='\0';
          strcpy(vids[i].actor, buff);
        }
      }
      printf("\n\t  Enter new rating [0-6, or <enter> to leave it 
    
    unchanged]: ");
      fgets(buff, sizeof(vids[0].rating), stdin);
      if(strlen(buff) >1) { 
        sscanf(buff, "%d", &vids[i].rating);
      }
      sortRecords(vids);
      return i;
    }
    int fetch(vid *vids, char *buff) {
       int i=0, lo, hi, mid;
       while(vids[i++].title[0]=='\0'); //find lo
       lo=i-1;
       hi = MAXVIDS;
       while(lo <= hi) {
         mid=(lo + hi)/2;
         if(strcmp(vids[mid].title, buff) >0)
           hi=mid-1;
        else if(strcmp(vids[mid].title, buff) <0)
           lo=mid+1;
        else
          return mid;
      }
      return 0;
    }
    int idelete(vid *vids) {
       char buff[80];
       int i;
       printf("\nEnter the title of the vid you want to delete:  ");
       fgets(buff, MAXTITLE, stdin); 
       buff[strlen(buff)-1]='\0';
       i = fetch(vids, buff); //get buff's index in the array
       if(i) {
         printf("Delete this record [y/n] ? ");
         printf("\n\t Title: %s \n\t Actor: %s \n\t Rating: 
    
    %d",vids[i].title,\
         vids[i].actor, vids[i].rating); 
         fgets(buff, 2, stdin);
         if(buff[0]=='y' || buff[0]=='Y') {
           printf("\nHas been deleted");
           getch();
           getchar();
           //*vids[i].title = "";
           //*vids[i].actor = "";
           //*vids[i].rating = 0;
         }
         return i;
       }
       else
         return 0;
    }
    void menu(vid *vids, int vidNum) {
      char ch;
      char buff[80];
      int i, n, index, ok=0;
      FILE *fp;
      do {
        printf("\n                 <<<<      Welcome to the Main 
    
    Menu       >>>>");
        printf("\n\n\t  There are %d videos in the library     \n", 
    
    vidNum);
        printf("\n\t    [a]dd a videos           [d]elete a videos   
    
       \n");
        printf("\n\t    [e]dit a videos          [f]etch a videos    
    
       \n");
        printf("\n\t    [v]iew all videoss       [w]rite all videos  
    
       \n");
        printf("\n\t\t   Make a choice or Q to quit: ");    
        fgets(buff, sizeof(buff), stdin);
        ch=buff[0];
        switch (ch) {
          case 'a':   
            printf("\nWould you like to add a new vid? [y/n]: ");
            ch=getchar();
            (void) getchar();
            if(ch=='y' || ch=='Y') {
              n=addVideos(vids);
              if(n==0) {   //a successful add
                sortRecords(vids);
                ++vidNum;
              }
            }
                    break;
          case 'd': ok=idelete(vids); 
                    if(ok)
                      --vidNum;
                    break;
          case 'e': edit(vids); 
                    break;    
          case 'f':  
            printf("\nEnter the title of the vid:  ");
            fgets(buff, sizeof(vids[0]), stdin); 
            buff[strlen(buff)-1]='\0';
            index = fetch(vids, buff);
            if(index) {
              printf("\n\t  Title: %s\n\t Actor: %s\n\t  Rating: 
    
    %2d",
              vids[index].title, vids[index].actor, 
    
    vids[index].rating);
              getch();
            }
            break;
          case 'v':  viewAll(vids); break;
          case 'w':  writeAll(vids, vidNum); break;
    
          case 'Q':  ch='q';
          case 'q':  break;
          default: printf("\n\t\t    Please enter a valid 
    
    selection\n");
        };
    
      }while(ch!='q'); 
    }
    
    void sortRecords(vid *vids ) {
      int i, j, lo = 0, hi=MAXVIDS; 
      vid vid1;
      char val[MAXTITLE];
      j=hi;   
      for(i=lo+1;i<hi;i++) {  
        vid1 = vids[i];
        j = i-1;
        while((strcmp(vids[j].title, vid1.title)) > 0) {
          vids[j + 1] = vids[j];
          --j;
          if(j<0) break;
        }   
        vids[j+1] = vid1;
      }
      printf("\n In sorted order: \n");
      for(i=0;i<10;i++)  {
        if(vids[i].title)
          printf("\nTitle: %s  Actor: %s  Rating: 
    
    %2d",vids[i].title,vids[i].actor,vids[i].rating);
      }
    }
    void viewAll(vid *vids) {
      int i=0;
      puts("\n\tHere is the list of your vids:\n");
      while(vids[i].title[0]) {
        //fwrite(&vids[i], Size, 1, stdout);
        printf("\n Title: %s\n Actor: %s\n Ratings: 
    
    %2d",vids[i].title, vids[i].actor, vids[i].rating);
        ++i;
      }
      putchar('\n');
    }
    int writeAll(vid *vids, int vidNum) {
      int i;
      FILE *fp;
    
      unlink("videos.bak");
      if(rename("videodb.txt", "videodb.bak")==0) {
        if((fp=fopen("videodb.txt", "wt"))==NULL) {
          printf("\nError opening videos.txt file. No records were 
    
    written.\n");
          return 1;
        }
      }else {
        printf("\nError renaming in function writeAll()\n");
        return 1;
      }
    
      i=0;
      while(1) {
        fprintf(fp, 
    
    "%s\n%s\n%d\n",vids[i].title,vids[i].actor,vids[i].rating);
        ++i;
        if(i>vidNum) break;
      }  
      fclose(fp);
    
      return 0;
    }

  4. #4
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    2.) How would I make it so that the names are stored in the text file alphabetically, so that when they are all listed in my lookup function, they will be in alphabetical order. I understand that I can use stricmp, but how do I manipulate a text file so that I can insert names and space down other names when needed? Is this possible?
    There are basically two ways to store the data:
    1) In sorted order - used for a small to medium amount of data OR when the raw data has to be used directly, for us poor humans. The sorting function in the above program, is one of the best for this, btw. In a list that is almost sorted, and small to medium in size, Insertion sort is FINE.

    2) For larger, more serious programs, the data won't be kept in sorted order, since it is changing all the time (think of the thousands of changes made every day to every city's phone book), and the massive job of sorting and resorting it, every hour of the day.

    What they do is, add data to the end of the file. Never sort the data. Instead, they sort an *index* (indeces are powerful things!), and then the data can be view, printed, edited, etc., in sorted order, by viewing it/printing it/etc., THROUGH the index.

    Naturally, a large amount of records can't be kept and handled, just in an array - array's are too limited in size. Records are kept on a disk drive, (as CT mentioned above), and retrieved (again, using the index, which is in memory and backed up onto the hard drive).

    Where the dividing line is between these two methods, depends on your computer system's RAM, etc. With 2-4GB of RAM, you should have little trouble with 2-10 thousand records in an array, depending on the size of each record, of course. After 10,000 records, it's to the file with those records, imo.

    The key thing is that using the index, there is no data shuffling needed when an edit/addition or deletion, is made. Edits are made "in place", right on the file, and new records appended, only. Deleted records aren't "deleted", they're just marked available for overwrite, and re-used, if available, when needed.

    Minimal movement of the data, keeps it safer, your maintenance costs lower, and your records more up to date, and accessible.

  5. #5
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    I suggest learning to use SQLite. It is just a matter of including the one header file, compiling and linking the one source file, learning the interface, a little SQL and relational database design (of which you need almost nothing in your case), and then you can do what you want more readily, along with the benefits that Adak described for "for larger, more serious programs".
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  6. #6
    Registered User
    Join Date
    Dec 2009
    Posts
    23
    Thanks for the input fellas.

    To Tater: I haven't gotten into studying structures or linked lists yet, but I will very soon.

    Adak, very good information. You make me wish I would have found this board sooner.

    I wasn't aware that fgets adds a newline charc to the end of a string. I also wasn't aware that something as simple as inserting a getchar after a scanf could solve such a cumbersome problem.

    I used your advice to solve my problems. Previously, I tried to use some sscanf functions in my add function, but fgets and getchar were giving me too many problems. Now that I've solved those problems, I have implemented that idea. I have also straightened out my indents as you've suggested.

    Here is my add function:

    Code:
    add()
    {
      FILE *pt;
      char word[25],word2[25],word3[25],word4[25];
      char name_number[100];
      word3[0] = 0,word4[0] = 0;
    	
      pt = fopen ("phonebook.txt","a");
      if (pt == NULL){	
        printf("NO PHONE BOOK FOUND!\n");
    	return 0;
      }
      printf("            *****************ADD ENTRY*****************\n\n");
      printf("Add in this format, e.g. Joe Smith 555-555-5555\n\n\n");
      printf("Add a name and a number now: \n\n");
      
      fgets(name_number,sizeof(name_number),stdin);
      name_number[strlen(name_number)-1] = '\0';
      sscanf(name_number,"%s %s %s %s",word,word2,word3,word4);
      
      fprintf(pt,"\n"); 
    
      if ( word3[0] == 0){
        fprintf(pt,"%s %s",word,word2);
      }
      else if (word4[0] == 0){
        fprintf(pt,"%s %s %s",word,word2,word3);
      }
      else {
        fprintf(pt,"%s %s %s %s",word,word2,word3,word4);
      }
      printf("\nNumber added successfully!\n\n");
      fclose(pt);
      
      getch();
      add2();
    } /*******END OF ADD FUNCTION********/
    Adak, as for the data storage methods that you've described, I will have to look into that at a later time because it seems a bit ahead of what I should be learning right now. Thanks, though.

    I will move on to studying structures shortly.

    Thanks again guys.

    ~Billy

  7. #7
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    You're quite welcome. All the best, BillyBob!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. How to remove Scrollbars from a C# Console App?
    By Gingerage in forum C# Programming
    Replies: 1
    Last Post: 09-26-2008, 03:50 AM
  2. Console app termination
    By C+/- in forum C++ Programming
    Replies: 2
    Last Post: 05-24-2008, 11:29 AM
  3. Console App Question
    By Sentral in forum C++ Programming
    Replies: 11
    Last Post: 07-21-2005, 12:37 PM
  4. Phone Book
    By FerrariF12000 in forum C Programming
    Replies: 7
    Last Post: 12-10-2003, 12:07 AM
  5. Turning a Console APP into Windows.
    By Darkflame in forum C++ Programming
    Replies: 3
    Last Post: 09-14-2001, 03:10 AM