Thread: Iterating through elements in an external .txt file help.

  1. #1
    Registered User
    Join Date
    Jul 2013
    Posts
    41

    Iterating through elements in an external .txt file help.

    Hi all - new to posting so bare with me if it seems a little abstract.
    Basically I am having trouble with comparing a string from input, with the elements in a text file. Now I have done this once already, however with only one element in the txt file.

    example code:

    Code:
    fp_pass = fopen ("pass.txt" , "r");
    if(fp_pass == NULL) perror ("Error opening file");
    else
    {
    fgets(buff,8,fp_pass);
    buff[8] = '\0';
    }
    do{
    printf("\n Enter the password : ");
    scanf("%s",password);
    if(strncmp(buff,password,8))
    {
    printf ("\n Wrong Password \n");
    i_retry++;
    printf ("\n You have %d try's left \n",3 - i_retry);
    }
    else
    {
    pass = 1;
    }
    A little something like this, gets the string from the external txt file, and compares it with user input. Problem I have been having is that now I would like to loop it so that the entered 'Password' can be compared against a list of many 'Passwords'.

    I think where my understaing breaks down is the while(!feof) or EOF should be used and why - Something about 16/32 bit informtion or so I have read.

    Thanks in advance for any help.

  2. #2
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    You're going to have to use dynamic memory allocation for this. You could use a char ** variable and allocate space for char *'s to it, but if you're not ready for this, I wouldn't try it just yet. You have to realize that since a file most likely wouldn't be static, you have to make the way you get data dynamic. Which means you can't get one file line, or there would be absolutely no point in the rest of the file.

    As far as you not understanding EOF, I like to think of it as just End Of File. Basically, it's a constant that's returned when there is no longer any more data for you get with fgets(). Which means you're at the end of that file stream. Without EOF, you wouldn't know when to stop retrieving data at all unless you asked the user how many lines there were, and even then the user could make mistakes.

    As far as your sample code, are you sure that each password will only need to be 8 characters? You put absolutely no limit on user input, that could lead to several bugs.

  3. #3
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    Right - I have recently been looking into/learning about dynamic memory allocation, which ironically whats brought me to this current conundrum. in pseudo code what I need to do is:

    get input from user.
    check against first element in external file.
    If matches - great
    if no match, try nest element in external file.
    if no match by end of file, return failure.

    Thing is in my head it sounds easy, since its effectively just an array of strings, just held externally, so all i want to do is go through the array as many times as (array.length) and compare with scanf(input) - right?


    as for the password thing, that was just a wuick example I typed up. But surely wouldn't the fact that i've nullified the final element in the buffer deal with any buffer overflow issues? I thought by using strncmp function, it was safe, if not, could you please give me a pointer as to whats wrong there please. (I am currently trying to find software similar to valrgrind to use with my compiler to highlight such issues, so probably still not picking up all errors with my naked eye.)

    Thanks

  4. #4
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    If you'll make up a test case file for the passwords, I'll help you get this set up.

    How many passwords do you expect to have (high guess please)? How long can a password be?

  5. #5
    Registered User HelpfulPerson's Avatar
    Join Date
    Jun 2013
    Location
    Over the rainbow
    Posts
    288
    Quote Originally Posted by stach View Post
    Right - I have recently been looking into/learning about dynamic memory allocation, which ironically whats brought me to this current conundrum. in pseudo code what I need to do is:

    get input from user.
    check against first element in external file.
    If matches - great
    if no match, try nest element in external file.
    if no match by end of file, return failure.

    Thing is in my head it sounds easy, since its effectively just an array of strings, just held externally, so all i want to do is go through the array as many times as (array.length) and compare with scanf(input) - right?


    as for the password thing, that was just a wuick example I typed up. But surely wouldn't the fact that i've nullified the final element in the buffer deal with any buffer overflow issues? I thought by using strncmp function, it was safe, if not, could you please give me a pointer as to whats wrong there please. (I am currently trying to find software similar to valrgrind to use with my compiler to highlight such issues, so probably still not picking up all errors with my naked eye.)

    Thanks
    You would think that, but imagine this situation. When you give scanf() or any user-input function a buffer to store input in, it will only store what it can. The rest will stay on the buffer that stores the user-input for future functions to collect. Basically, if you made another call to get the next password, what you would be getting is anything that's left on the buffer. If the user entered a 100 character string for some reason, then that would mess up any more calls for input, since they would still be grabbing those 100 characters. Here is what I would do for getting one line from a file.

    Code:
    char * GetNextFileLine( FILE * music_file )
    {
        char * line = malloc(sizeof(char) * BUFSIZ );
        char * newline;
    
    
        if ( line == NULL )
        {
            perror("Malloc");
            exit(1);
        }
    
    
        if ( fgets(line, BUFSIZ, music_file) == NULL )
        {
            free(line);
            return NULL;
        } else {
            if ((newline = strchr(line, '\n')) != NULL ) /* Removes the '\n' and replaces it with a '\0' if it is found */
                *newline = '\0';
    
    
            return line;
        }
    }
    That gives the user a lot leniency on what they can put in, and if it's not valid, I can still handle accurately. I also prevented invalid memory bugs by allocating memory to a character pointer in the function, then returning that pointer or NULL if EOF or an error occurs. It also checks if there is a newline character and replaces it with a terminating character. You can use this function if you want, and you would be able to find passwords in it with strstr() or get more advanced by separating the text into tokens. Make sure to free the memory( Valgrind should tell you if you have a leak ) when you're done with the string though.

  6. #6
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    Well to be honest, I'm only doing this for the sake of learning how to do this. It's not going to have any real application, I just like to learn and practice ideas for the sake of my coding ability an future. Lets just say theres 10 different passwords, of maximum length 8 bits long. Like I said before, I could quite easily do this as an array initialised in the main class and iterate through each element in that array, but I would like to learn how to do this with an external file.
    Code:
    char pass[8];
            char input[8];
            int found = 0;
            fp_pass = fopen ("pass.txt" , "r");
            if(fp_pass == NULL) perror ("Error opening file");
    
            while(!feof(fp_pass))
            {
                    fgets(pass,8,fp_pass);
                    pass[8] = '\0';
                    do{
                    printf("\n Enter the password : ");
                    scanf("%s",input);
                    if(strncmp(pass,input,8))
                    {
                            printf ("\n Wrong Password \n");
                            i_retry++;
                            printf ("\n You have %d try's left \n",3 - i_retry);
                    }
                    else
                    {
                            found = 1;
                    }
            }
    So the problem I see here is that I lack the knowledge of how this that I have just mocked up would actually increment through (lets say 10) elements in the external file, if in fact it would at all (probably not). As i've said, I can use and retrieve data from an external file, but only when that file contains one element. Now if it contains 10, how do I iterate through those 10 elements?

    Thanks

  7. #7
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    If the data is PERFECTLY formatted, then you can use fscanf (), to read through the entire file contents.

    If the data is not perfectly formatted, then you might want to use either special format specifiers in fscanf(), or use fgets(). (fgets() requires string.h be included). Also, fgets() adds a newline char at the end of each line, so you'll want to remove it, before you try to make any string comparisons.

    This code shows how:
    Code:
    char passwords[10]; //just a scooch more room
    int len;
    
    //open the file, etc.
    //then
    while((fgets(passwords, 10, fp) !=NULL) {
       //remove the newline at the next to last char position
       len=strlen(passwords) - 1;
       if(passwords[len]=='\n')
          passwords[len]='\0'; //overwrite the newline with the end of string marker
    }
    
    //now string comparison functions will work correctly.
    So you could open the file in reading mode, read through all the data, and then do a rewind(fp), where FILE *fp is the file pointer you used to open the file with. The obvious problem is that it's way slower than working from memory, and the data in the file probably will be listed in the order it was entered, rather than in sorted order.
    Data in a sorted order, makes VERY fast searching, very easy to do (binary search).

    In general when you deal with data on some other device, (hard drive, static drive, tape drive, etc.), you want to access it conservatively, preferring to load the data into memory, and work with it there. It's much faster, it doesn't rely on less reliable devices, and it runs (almost) zero risk that you will have a bug in the program, that might result in damaging valuable data in the file.

  8. #8
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    Thanks Adak - Much appreciated. I'll give it another bash tomorrow at work, using your ideas above and I'll post the outcomes.

    Thanks Again.

  9. #9
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    There is nice explanation on using fgets in the FAQ

    FAQ > Get a line of text from the user/keyboard (C) - Cprogramming.com

    Have you read it and tried the suggested samples for yourself?
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  10. #10
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    OK - after implimenting Adak's suggestion into the function, it was nearly there, I could feel it, the only amendment I made was to put the searching of the file into a for loop of length equal to the number of elements in the external file. Without that code, I was comparing the input of the user against the first element, if it was wrong, it would goto the next element but not comapre against the given input.

    So working to desired expecations now, the only downside I can see is what was previously mentioned by HelpfulPerson which is regarding dynamic memory allocation since the code only works when given the exact length of the file, whereas I'd now like to impliment adding new passwords to the external file so the length of the file will differ - so thats the next step, of which any suggestions of correct practice in this respect would be more than welcome.

    Thanks

  11. #11
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    Quote Originally Posted by vart View Post
    There is nice explanation on using fgets in the FAQ

    FAQ > Get a line of text from the user/keyboard (C) - Cprogramming.com

    Have you read it and tried the suggested samples for yourself?
    The problem I have found with this example is that when I replicate it almost code for code, It doesn't wait for input from the user, but simply returns the whole code output, as if nothing was input...?

  12. #12
    Registered User
    Join Date
    Sep 2006
    Posts
    8,868
    This shows an easy but good way to handle it, for a small to medium sized password file, without using a linked list.

    Linked lists are the classic way to handle an unknown number of input items, but they are time consuming to work with.

    You have a file with 26 passwords: passwords.txt, in the same directory.

    Code:
    #include <stdio.h>
    #include <stdlib.h> //for malloc()
    #include <string.h> //for strlen()
    
    int main(void) {
       int i, len, passNum=0;
       char passWord[30]={'\0'};
       char **A;
       FILE *fp=fopen("passwords.txt", "r");
       if(!fp) {
          printf("Error! Password file did not open\n");
          return 0;
       }
       //count the number of passwords
       while((fgets(passWord, 30, fp)) != NULL) 
         ++passNum; 
    
       printf("There are %d passwords in the file\n",passNum);
       rewind(fp); //move the file pointer, back to the beginning
    
       //allocate the password array (2D)
       A = malloc(passNum * sizeof(char*));
       for(i=0;i<passNum;i++) {
          A[i]=malloc(30); //each password can have 29 chars in it.
       }
       
       for(i=0;i<passNum;i++) {
          fgets(A[i], 30, fp);
          printf("%s",A[i]);
          len=strlen(A[i])-1;
          if(*A[len]=='\n')  //if present, remove the newline
             *A[len]='\0';   //from the end of the password
         //strcmp() calls can work properly now
       }
       
       fclose(fp);
       printf("\n\n");
       return 0;
    }
    Contents I used for passwords.txt file:
    alpha
    beta
    charlie
    delta
    echo
    foxtrot
    golf
    hotel
    India
    Juliet
    kilo
    Lima
    Mike
    November
    Oscar
    Papa
    Quebec
    Romeo
    Sierra
    Tango
    uniform
    victor
    whiskey
    x-ray
    yankee
    zulu

  13. #13
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    Quote Originally Posted by Adak View Post
    [code]
    //count the number of passwords
    while((fgets(passWord, 30, fp)) != NULL)
    ++passNum;
    Great, I like this bit because thats pretty much the amendment I made earlier. What about this fgets then to get user input instead of scanf, whats that all about, never works for me, thats why i use scanf?

    My idea is that i'm going to use what i've learned here to redo the project since its got messy and expensive (in many ways). You'd think i'd know all this stuff!?!?

  14. #14
    Registered User
    Join Date
    Jul 2013
    Posts
    41
    Yeah, any use of strcmp or strncmp I try to impliment now just creates a segmentation fault.

    I've tried clearing the buffer after every call to get next password.
    I've trid copying both in put and password to local array using strcpy just to make comparing easier - nope....

    Running out of ideas guys....i'll post some more code up tomorrow, works out in 2 mins and I'm ready for a lye down .

    Any Help would be mega, thanks!
    Last edited by stach; 07-30-2013 at 10:29 AM.

  15. #15
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    What is your current code, and how does it not work?
    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

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Replies: 14
    Last Post: 12-02-2007, 03:32 AM
  2. opening an external file
    By kpwd in forum C++ Programming
    Replies: 10
    Last Post: 11-24-2002, 10:43 AM
  3. running a external file
    By krappykoder in forum C++ Programming
    Replies: 5
    Last Post: 06-14-2002, 07:19 PM
  4. getting variables from an external .cfg file
    By Ion Blade in forum C++ Programming
    Replies: 3
    Last Post: 05-20-2002, 07:35 PM
  5. Sum of external file
    By robjules in forum C++ Programming
    Replies: 7
    Last Post: 05-08-2002, 09:35 PM

Tags for this Thread