0

I'm writing a program that simulates a platform where users can register. In register function I ask the user for his username and the program should create a new file with username as file name to store his password and other data for other functions. Problem here is that fopen returns NULL which means that it can't be created.

void Register()
{
char name[11];
char password[11];
char aux[11];
system("cls");
FILE *fp=fopen("Users.txt","a+"); //I've created this .txt, just has one user which is admin
if (fp==NULL){
    printf("\nFile cant be opened");
    getchar();
    return;
}
printf("Write a new username (no more than 10 characters, no spaces) ");
fflush(stdin);
fgets(name,sizeof(name),stdin);
getchar();
do{
    if((fgets(aux,sizeof(aux),fp))!=NULL){ //Checks is reading lines
        if ((strcmp(name,aux))==0){ //Username already exists
                printf("\nUsername already exists, try another: ");
                fflush(stdin);
                fgets(name,sizeof(name),stdin);
                rewind(fp); //Takes pointer to the beginning
        }
    }
}while(!(feof(fp))); 
fseek(fp,0,SEEK_END); //Takes pointer to end
fprintf(fp,"%s",name);
fclose(fp);
fp=fopen(name,"w"); /*THIS IS WHERE IT FAILS, RETURNS NULL*/
if (fp==NULL){
    printf("\nFile cant be opened");
    getchar();
    return;
}
printf("\nChoose a password(no more than 10 characters): ");
fflush(stdin);
fgets(password,sizeof(password),stdin);
getchar();
fprintf(fp,"%s\n",name);
fprintf(fp,"%s",password);
fclose(fp);
printf("\nUser successfully registered\nName: %s\nPassword: %s",name,password);
getchar();
}

I've already tried with another method. For example, use strcpy() to copy name to a new array and then strcat() to add ".txt" but it doesn't work either. Like this:

[...]
char aux2[20];
strcpy(aux2,name);
strcat(aux2,".txt");
fp=fopen(aux2,"w"); /*FAILS TOO */
if (fp==NULL){
    printf("\nFile cant be opened");
    getchar();
    return;
}

Can't figure out what is going wrong. Hope you can help me

Jonas M
  • 35
  • 1
  • 7
  • 4
    Please don't use `fflush(stdin)`. Calling `fflush` on an input-only stream (like `stdin)` is explicitly undefined behavior in the C specification. If you want to write portable code don't do it. – Some programmer dude Mar 19 '17 at 17:23
  • I've checked the while(!(feof(fp))) condition, and it's working correctly – Jonas M Mar 19 '17 at 17:35
  • 1
    You know that fgets leaves the newline character in the line read, right? It's not illegal to create a file with a newline in the name but it is likely to cause problems. For your actual problem, I suggest you use `perror` to get a hint why the fopen fails. – rici Mar 19 '17 at 17:49
  • @ryyker `while(!(feof(fp))) { ... }` is not the same as `do { ... } while (!(feof(fd)));`. The `do {...} while();` loop in this question is perfectly valid because the conditional clause is evaluated *after* the EOF condition is reached, unlike `while() {...}`, where the fact that `feof()` is evaluated *before* the EOF condition is reached, forcing an extra loop iteration. – Andrew Henle Mar 20 '17 at 09:29
  • @AndrewHenle - Thanks. Good clarification. deleted my comments. – ryyker Mar 20 '17 at 13:18
  • when using `fgets()` to input the password, the trailing newline must be removed perhaps by: `char *newline; If( (newline = strchr( password, '\n' ) ) ) { *newline = '\n' ); }` – user3629249 Mar 20 '17 at 14:45
  • @JonasM, using `feof()` to control a loop does not work, Suggest reading: `http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong` – user3629249 Mar 20 '17 at 14:49
  • for ease of readability and understanding: 1) consistently indent the code. indent after every opening brace '{'. unindent before every closing brace '}'. Suggest 4 spaces for each indent level as that is visible even with variable width fonts. 2) separate code blocks (for, if, else, while, do...while, switch, case, default) via a single blank line – user3629249 Mar 20 '17 at 14:52
  • error messages should be output to `stderr`, not `stdout` and if that error is the result of a call to a system function, then also output the message related to why the OS thinks the error occurred. That is accomplished by calling `perror()`. – user3629249 Mar 20 '17 at 14:54
  • the posted code seems to be expecting that a signal or a I/O error will never occur when reading an input. The code block beginning with `do {` will not work correctly under such an event. suggest replace the `d0 { ... while( feof(fp);` with `while( fgets(aux,sizeof(aux),fp) ) { ... }` – user3629249 Mar 20 '17 at 15:00
  • the line: `system( "cls" );` is not portable Strongly suggest using the ANSII escape sequences to clear the screen and return the cursor to the upper left corner. – user3629249 Mar 20 '17 at 15:02
  • the call to `fgets()` input the whole line, include the newline sequence, the following call to `getchar()` requires the user to enter another 'enter' key stroke – user3629249 Mar 20 '17 at 15:04
  • the output streams are buffered, so a call to `printf()` will not output anything to the terminal until a '\n' output or a call to `fflush( stdout );` or the code calls an input function – user3629249 Mar 20 '17 at 15:08
  • the posted code indicates that the user name is the first entry on each line in the input file, BUT these two lines: `fprintf(fp,"%s\n",name); fprintf(fp,"%s",password);` are alternating lines with a password and a name. Probably not what you want. – user3629249 Mar 20 '17 at 15:25

3 Answers3

1

List of problems:

  1. You have not declared fp (at least in shown code) before trying to use it. Change line to:

    FILE *fp=fopen("Users.txt","a+");

  2. You have not declared nombre. Add line:

    char nombre[11];//to match size of name

(Or, you may have meant to use name?)

  1. Everywhere it exists in your code, comment out the line:

    //fflush(stdin);

  2. Change the line

     fp=fopen(name,"w"); /*THIS IS WHERE IT FAILS, RETURNS NULL*/ 
    

to:

fp=fopen(name,"w+"); /*THIS IS WHERE IT FAILS, RETURNS NULL*/
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • I did declare fp outside of function, globally. And sorry for the "nombre", I was rewriting code as it was in my native language, edited already – Jonas M Mar 19 '17 at 17:38
  • You should really include a _[complete code segment](http://sscce.org/)_ to enable people helping to be able to. – ryyker Mar 19 '17 at 17:57
0

Just figured it out, I had to change the line

if((fgets(aux,sizeof(aux),fp))!=NULL){ //Checks is reading lines

And instead use fscanf() just like this:

if((fscanf(fp,"%s",&aux)==1){ //Checks if is reading a string (a line)

Jonas M
  • 35
  • 1
  • 7
  • 1
    when using the `%s` input conversion specifier, always use a MAX CHARACTERS modifier that is one less than the length of the input field to avoid any chance of a buffer overflow. Such overflow would be undefined behavior and can/will lead to a seg fault event. – user3629249 Mar 20 '17 at 15:59
0

the question was not clear about the actual format of the data within the file. So I decided to use a format of name password for each line of the file.

Here is a version of the code that cleanly compiles and performs the desired function.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void Register( void );

void Register( void )
{
    char name[11];
    char password[11];
    char aux[11];

    system("cls");

    printf("Write a new username (no more than 10 characters, no spaces) ");
    if( !fgets(name,sizeof(name),stdin) )
    {
        fprintf( stderr, "fgets for user name failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, fgets successful

    // code should remove trailing new line, if any
    char *newline;
    if( (newline = strchr( name, '\n' ) ) )
    {
        *newline = '\0';
    }

    FILE *fp=fopen("Users.txt","a+"); //I've created this .txt, just has one user which is admin
    if ( !fp)
    {
        perror("fopen for Users.txt for read/write failed");
        getchar();
        exit( EXIT_FAILURE );
    }

    // implied else, fopen successful

    while( fgets(aux,sizeof(aux),fp) )
    { //Checks is reading lines
        // separate out the name field
        char *token = strtok( aux, " " );
        if( token )
        {
            if ((strcmp(name,aux))==0)
            { //Username already exists
                printf("\nUsername already exists, try another: ");
                rewind(fp); //Takes pointer to the beginning
            }
        }
    }

    // name not already in file

    printf("\nChoose a password(no more than 10 characters): ");

    if( !fgets(password,sizeof(password),stdin) )
    {
        perror( "fgets for password failed" );
        fclose( fp ); // cleanup
        exit( EXIT_FAILURE );
    }

    // implied else, fgets successful

    //remove trailing newline
    if( (newline = strchr( password, '\n' ) ) )
    {
        *newline = '\0';
    }

    fprintf(fp,"%s %s\n",name, password);

    fclose(fp);

    printf("\nUser successfully registered\nName: %s\nPassword: %s",name,password);
    getchar();
}  // end function: Register
user3629249
  • 16,402
  • 1
  • 16
  • 17