So my assignment is to create a login system. Username and passwords will be checked with content in the “account.txt” file. The content is that file have structure look like this:
Account ID: 1
Name: John Lee
Pass: 7uf
Role: student
Account ID: 2
Name: Park Lee
Pass: 42h
Role: Lecturer
Here what i got so far:
struct Account {
char name[20];
char pass[20];
};
void Login (char name[], char pass[]){
FILE *sc;
struct Account acc;
sc = fopen("Account.txt","r");
fscanf(sc,"\nName: %s",acc.name);
fscanf(sc,"\nPass: %s",acc.pass);
if(strcmp(name,acc.name) == 0 && strcmp(pass,acc.pass)) {
printf("Login successful");
}
else {
printf("Name or Pass incorrect");
}
fclose(sc);
}
int main () {
struct Account log[20];
fflush(stdin);
printf("\n\t\tEnter your name: ");
gets(log[20].name);
printf("\t\tEnter your password: ");
gets(log[20].pass);
Login(log[20].name,log[20].pass);
}
return 0; }
What do you guys think i should do ?
in the function: login() the code needs to check every entry in the file before declaring a failure. After all, the first entry might not be for the person trying to login
regarding:
sc = fopen("Account.txt","r");
fscanf(sc,"\nName: %s",acc.name);
1) always check (!=NULL) the returned value from fopen() to assure the operation was successful.
2) need to move past the first line of each entry in the input file before trying to read the name
3) when using the input format specifiers '%s' and/or '%[...]' always include a MAX CHARACTERS modifier that is 1 less than the length of the input buffer because those specifiers always append a NUL byte to the input. This avoids a buffer overflow and the resulting undefined behavior.
I.E.
if( !sc )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
{input first line of acct and discard}
if( fscanf(sc,"\nName: %19s",acc.name) != 1 )
{
// handle error
}
However, if those lines in the input file contains those labels, like Name: Then the code needs to also input and discard those labels, as in the above example.
This seems to be homework, so I'm very reluctant to just 'give' you appropriate code. I would expect your instructor or TA would be able to help you with the details of what the code should be doing.
regarding statements like:
gets(log[20].name);
1) gets() is no longer part of the C language, Your compiler should have told you this.
2) the valid index into an array has the range: 0...(number of entries in array -1). So index 20 is beyond the end of the range. Suggest just using a pointer to the array.
3) Suggest using `fgets() to input each line from the file.
4) the struct you have declared will not work well with the actual data from the input file.
Suggest using:
#define MAX_LOG_ENTRIES 20
int main( void )
{
struct Account acc[ MAX_LOG_ENTRIES ] = { "","" };
char dummy[128];
size_t i;
for( i = 0; i<MAX_LOG_ENTRIES; i++ )
{
if( i< MAX_LOG_ENTRIES && fgets( dummy, sizeof( dummy ), sc ) )
{ // then successfully read 'account' line
if( fgets( dummy, sizeof( dummy ), sc ) )
{ // then successfully read 'Name:` line
// remove trailing newline
dummy[ strcspn( dummy, "\n" )] = '\0';
// skip past Name: ' label
char * namePtr = strchr( dummy, ':' );
if( namePtr )
{ // then found the ':'
// step by ': '
namePtr += 2;
}
// extract name
strcpy( log[i].name, namePtr );
if( fgets( dummy, sizeof( dummy ), sc ) )
{ // then successfully read 'Pswd:` line
// remove trailing newline
dummy[ strcspn( dummy, "\n" )] = '\0';
// skip past Pswd: ' label
char * pswdPtr = strchr( dummy, ':' );
if( pswdPtr )
{ // then found the ':'
// step by ': '
pswdPtr += 2;
}
// extract password
strcpy( log[i].pswd, pswdPtr );
// read/discard unused data line
fgets( dummy, sizeof( dummy ), sc );
// read/discard unused blank line
fgets( dummy, sizeof( dummy ), sc );
}
When the above for() loop exits, all the records are read into the array named log[] and the variable 'i' contains the number of entries in the array 'log[]' that are actually used
now the code needs to input the two fields from the user (name and pswd)
Then loop through the array log[] to see if there is a 'name+pswd' match.
if fgets( dummy, sizeof( dummy ), sc );a match is found, then success, otherwise the user failed to enter valid data.
Note: The above code fails to check for errors and similar problems, including if the input file contains less than 20 entries. You should be able to add the error (and EOF) checking
Related
I am very new to the C programming language and I am trying to make a login thing, the problem that I am having is that I can't get the user_len to compare in my while statement.
Thank you for any and all feedback
EDIT:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct User_name{
int user_id;
char username[20];
char password[30];
} User_data;
//make all the variable
int i;
int user_len;
int pass_len;
//prototype function
char user ();
int user_name();
int main() {
//get username
user_name (User_data.username);
}
int user_name() {
while(user_len > 20) {
printf("Enter your username:\n");
scanf("%s",User_data.username);
user_len = strlen(User_data.username);
if (user_len > 20) {
printf("\nusername is too long please enter again:%d\n",user_len);
} else {
printf("Username is: %s\n",User_data.username);
}
}
return(user_len);
}
The content of the while loop will never be executed, because the condition in the line
while(user_len > 20) {
will be false at the start of the program, because user_len will have the value 0.
Also, the lines
char user ();
int user_name();
are not prototype declarations. They are forward declarations of functions that take an unspecified number of arguments. If you want to declare that they take no arguments, then you should change them to:
char user( void );
int user_name( void );
See the following question for more information:
Warning/error "function declaration isn't a prototype"
The following code has a serious problem:
printf("Enter your username:\n");
scanf("%s",User_data.username);
user_len = strlen(User_data.username);
if (user_len > 20) {
printf("\nusername is too long please enter again:%d\n",user_len);
} else {
printf("Username is: %s\n",User_data.username);
}
There is no point in checking for a buffer overflow after the buffer overflow has already occurred. The buffer overflow must be prevented in the first place, in order to prevent your program from invoking undefined behavior (i.e. to prevent your program from possibly crashing).
The easiest way to prevent the buffer overflow would be to change the scanf format string from "%s" to "%19s", which would limit the number of matched characters to 19, so that it will not write more than 20 characters to User_data.username (including the terminating null character).
However, this solution is not ideal, as it is possible that scanf will leave non-whitespace characters from that line on the input stream (this is also possible when using "%s"). These non-whitespace characters will likely cause trouble in the next loop iteration when scanf is called.
For this reason, it would probably be best if you used the function fgets instead of scanf. The function fgets has the advantage that it will always read exactly one line at a time (provided that the supplied input buffer is large enough to store the whole line), which is not necessarily the case with scanf.
Here is my solution to the problem, which uses fgets instead of scanf:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
struct User_name
{
int user_id;
char username[20];
char password[30];
} User_data;
//make all the variable
int i;
int user_len;
int pass_len;
//prototype function
int user_name( void );
int main( void )
{
//get username
user_name();
}
int user_name( void )
{
while( true )
{
char line[100];
char *p;
//prompt user for input
printf( "Enter your username: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
printf( "Error reading input!\n" );
exit( EXIT_FAILURE );
}
//find newline character
p = strchr( line, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( p == NULL )
{
//a missing newline character is ok on end-of-file condition
if ( !feof( stdin ) )
{
int c;
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Error reading from input\n" );
return false;
}
} while ( c != '\n' );
goto line_too_long;
}
}
else
{
//remove newline character by overwriting it with null character
*p = '\0';
}
//find length of string
user_len = strlen( line );
//check if length is acceptable
if ( user_len >= 20 )
{
goto line_too_long;
}
//username is ok, so copy it
strcpy( User_data.username, line );
//break out of infinite loop
break;
line_too_long:
printf( "Line was too long, try again!\n" );
}
//print username
printf( "Username is: %s\n", User_data.username );
return user_len;
}
This program has the following behavior:
Enter your username: ThisIsAVeryLongUserName
Line was too long, try again!
Enter your username: ShortUserName
Username is: ShortUserName
Note that this program uses one goto label and two goto statements. Normally it is a good idea to try to not use goto, but to use normal control flow statements instead. However, in this case, I believe that using goto was appropriate. The alternative would have been to duplicate the line printf( "Line was too long, try again!\n" ); and use a continue statement in two places in the program. I believe that it is better to handle this error in one place in the program (though I understand that this topic is highly controversial and that some people would consider it better to use code duplication).
Another issue worth mentioning is that you are using global variables (which I have taken over in my solution). This is considered bad programming style and should generally be avoided. Here is a modified version of my solution which avoids the use of global variables:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
struct user_data
{
int user_id;
char username[20];
char password[30];
};
//prototype function
int user_name( void );
int main( void )
{
//get username
user_name();
}
int user_name( void )
{
struct user_data ud;
int user_len;
while( true )
{
char line[100];
char *p;
//prompt user for input
printf( "Enter your username: " );
//attempt to read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
printf( "Error reading input!\n" );
exit( EXIT_FAILURE );
}
//find newline character
p = strchr( line, '\n' );
//make sure that entire line was read in (i.e. that
//the buffer was not too small)
if ( p == NULL )
{
//a missing newline character is ok on end-of-file condition
if ( !feof( stdin ) )
{
int c;
//discard remainder of line
do
{
c = getchar();
if ( c == EOF )
{
fprintf( stderr, "Error reading from input\n" );
return false;
}
} while ( c != '\n' );
goto line_too_long;
}
}
else
{
//remove newline character by overwriting it with null character
*p = '\0';
}
//find length of string
user_len = strlen( line );
//check if length is acceptable
if ( user_len >= 20 )
{
goto line_too_long;
}
//username is ok, so copy it
strcpy( ud.username, line );
//break out of infinite loop
break;
line_too_long:
printf( "Line was too long, try again!\n" );
}
//print username
printf( "Username is: %s\n", ud.username );
return user_len;
}
In this code, the problem which I am facing is, it only accepts the first word which has a comma at the end. The file has many words with commas at the end but it is accepting the first one. For example, if I gave the option to enter your ID card number which is not the first word. It could be the 2nd or 3rd word in the file then how I would handle it?
1st part
#define STRING_SIZE 49
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void RemoveNewLines( char * buffer ){
char * ptr;
ptr = strchr( buffer, '\n' );
if( ptr )
* ptr = 0;
ptr = strchr( buffer, '\r' ); // in case you see carriage returns also
if( ptr )
* ptr = 0;
}
2nd part
int main(){
char instr[STRING_SIZE+1], string[STRING_SIZE+1];
FILE * fr = NULL;
int flag = 0;
size_t length = 0;
fr = fopen("file.csv","r");
if( fr == NULL ){
printf( "Unable to open file\n" );
return 1;
}
printf("Enter your name: ");
fgets( instr, STRING_SIZE, stdin);
RemoveNewLines( instr );
strcat( instr, "," ); // append a comma to the user's entry
length = strlen( instr );
while( fgets( string, STRING_SIZE, fr ) ){
RemoveNewLines( string );
if( strncmp( instr, string, length ) == 0 ){
flag = 1;
break; } }
fclose(fr);
if( flag == 1 ) // I prefer positive logic{
printf( "Your details :" );
printf( "'%s'\n", string );
return 0;
}
printf("Access denied.\n");
return -1;
}
Well, you are comparing the beginning of a line in the file with whatever that was provided plus a , appended. So it is really what you asked it to do.
If you want to search on arbitrary fields, rather than this approach, I would split the line read from the csv, and compare the nth field with what was provided. Assuming this csv file is a basic one (no quotes or embedded commas/new-lines), you can easily do that by performing N strtok() operations on string.
So let's say that a file has multiply lines each containing one word. I want to store the characters of every word in every line in a array. The code below clearly doesn't work because the -i is zeroed in every loop and the program starts storing characters in the 1st position of the array again. The code is:
while(1)
{
if(fgets(str, 50, fp) == NULL)
break;
for(i=0; i<strlen(str); i++)
p[i] = str[i];
}
you have separate counter variable for p and keep incrementing it to avoid
overwritting, like below.
int write_position = 0;
while(1)
{
if(fgets(str, 50, fp) == NULL)
break;
for(i=0; i<strlen(str); i++)
p[write_position++] = str[i]; // you will not lose previous ones here
}
at the end lenght of the array p is equal to write_position
Fix your file reading loop like this;
while (fgets(str, sizeof(yourString), fp) != NULL)
{
yourString[strlen(yourString) - 1] = '\0'; // delete the new line
printf("%s\n", yourString);
}
So simply in the above code, your while loop will be working until there is no another line to read in the file. In each turn of while loop, it will take one line from your file, and add it to your yourString char array. Notice that, fgets() will be taking newline characters too (\n) for every line in the file, so that we need to remove this characters from the array before we add another line in it.
Before the while loop, You need to declare a char array to store each line in it such as;
char yourString[stringSize];
You need to determine a stringSize for your array to make it has enough storage for your file.
the following proposed code snippet
performs the desired functionality
assumes that the array 'p[]' is an array of char pointers to pointers and contains (at least) enough entries to hold all the lines in the input file
no line in the input file is longer than 48 characters + newline
And now the proposed code:
#define MAX_LINE_LEN 50
char **p;
p = calloc( NUM_LINES_IN_FILE, sizeof( char * ) );
if( !p )
{
perror( "calloc failed" );
exit( EXIT_FAILURE );
}
// implied else, calloc successful
FILE *fp;
if( !(fp = fopen ( "inputFileName", "r" )) )
{
perror( "fopen failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
char str[ MAX_LINE_LEN ];
for( int i=0; fgets(str, sizeof( str ), fp); i++ )
{
// remove trailing newline char
str[ strcspn( str, '\n' ) ] = '\0';
p[i] = strdup( str );
}
fclose( fp );
this is my source code. When I input a string "I am in CSE 2nd year." and replace CSE 2nd(ie loc 9 to 15) by ECE 3rd, I get some garbage vales at the end of string. Also there is a newline at the beggining of rslt2 string. There is something wrong with rslt2. Can anyone please rectify the error?
//splitting a string and replace latter part of string by another string
#include<stdio.h>
#include<string.h>
int main()
{
int i,count=0,loc2,scount=0,rcount=0,loc=0; //scount represents counter for subset and rcount for replacement and loc from where we will split the string
char str[100],sub[100],newss[100],rslt[100],rslt2[100]; //newss=new substr, rslt and rslt2=former and latter part of original string
printf("Enter a String:\n");
fgets(str,100,stdin);
printf("\nString Entered by User:\n");
fflush(stdin);
puts(str);
printf("\nLoc Char\n"); //Creates Colums 'Char' and 'Loc'
for(i=0;str[i]!='\0';i++)
{
count++; //Counts length of String
printf("%d. %c\n",count,str[i]); //Prints Characters with it its Corresponding Location
}
printf("\n\nLength of String: %d\n\n",count);
printf("Enter the locations of Characters from where subset will start and end: \n");
scanf("%d%d",&loc,&loc2); //stores indices of begining and end of substring
printf("\n\nSubset formed from Existing String:\n");
for(i=loc-1;i<loc2;i++)
{
scount++;
sub[i]=str[i]; //stores substring in "sub"
printf("%c",sub[i]);
}
printf("\n\nLength of Subset: %d\n",scount);
for(i=0;i<(loc-1);i++)
{
rslt[i]=str[i]; //Stores former part of string in resultant string
}
for(i=loc2;i<strlen(str);i++)
{
rslt2[i]=str[i]; //Stores latter part of string in resultant string2
}
printf("\n\nEnter a Replacement for Subset(Of Equal Length as that of Subset):\n");
fflush(stdin);
fgets(newss,100,stdin);
for(i=0;newss[i]!='\0';i++)
rcount++;
printf("\n\nLength of New Subset: %d\n",rcount-1); //-1 to subtract length of null char
if(rcount-1!=scount) //to check whether replacement string and substring are of same len
printf("\nSince length of both subsets is not same. \nHence Replacement is Not Possible\n");
else //Concatination of 3 substrings
{
printf("\nResultant String:\n");
for(i=0;i<(loc-1);i++)
printf("%c",rslt[i]);
printf("\n");
for(i=0;newss[i]!='\0';i++)
printf("%c",newss[i]);
for(i=loc2;rslt2[i]!='\0';i++)
printf("%c",rslt2[i]);
}
return 0;
}
Here's an example of how to implement that program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXL 100
int main( void )
{
int count, lengthI, lengthR, start, end;
char initial[MAXL], replacment[MAXL], range[MAXL], result[MAXL];
// get user input
printf( "Initial string: " );
fflush( stdout );
fgets( initial, MAXL, stdin );
printf( "Replacement string: " );
fflush( stdout );
fgets( replacment, MAXL, stdin );
printf( "Start and end: ");
fflush( stdout );
fgets( range, MAXL, stdin );
count = sscanf( range, "%d%d", &start, &end ); // indices of beginning and end of range in initial string
// remove newline character from the input strings, if necessary
lengthI = strlen( initial );
if ( lengthI > 0 && initial[lengthI - 1] == '\n' )
initial[--lengthI] = '\0';
lengthR = strlen( replacment );
if ( lengthR > 0 && replacment[lengthR - 1] == '\n' )
replacment[--lengthR ] = '\0';
// range checking to verify that user inputs are valid and the resulting string will fit into the buffer
if ( count != 2 || start < 0 || start > lengthI || end < start || end > lengthI )
{
fprintf( stderr, "Invalid start and end values\n" );
exit( 1 );
}
if ( lengthI + lengthR - (end - start) + 1 > MAXL )
{
fprintf( stderr, "Resulting string would be too long\n" );
exit( 2 );
}
// create a new string with the substring replaced
if ( start > 0 ) // copy characters from the initial string up to the start index
strncpy( result, initial, start ); // note: this step may leave the result string unterminated
strcpy( &result[start], replacment ); // append the repacement string
// guarantees the result string is terminated
if ( end < lengthI ) // append characters from the initial that are after the end index
strcat( result, &initial[end] ); // terminates the result string (provided that strcat is called)
// print the result
printf( "%s\n", result );
}
Comments:
Don't mix fgets and scanf. Even if you're aware of the issues involved with doing so, it's still easy to get it wrong. Best to just read lines with fgets, and then parse with sscanf as necessary.
fflush(stdin) is non-standard. fflush is only guaranteed to work on stdout. On some systems, fpurge can be used to erase unread input.
When dealing with strings in C (aka arrays of characters), range checking is essential. Buffer overruns are the #1 cause of crashes, unexpected behavior, and security holes in C programs. Always range check user inputs, and always verify that a newly created string will fit into the buffer provided.
Always make sure that a newly created string ends with a null character (aka '\0'), and be sure to include that null character when calculating buffer sizes.
Note that in the sample code, strncpy may leave the string unterminated. The subsequent call to strcpy will terminate the string. The strcat function will also terminate the string. However, the call to strcat is conditional based on user input. Hence, without the call to 'strcpy', we'd have extra work to do to guarantee the the string gets its mandatory null terminator.
use negated scanf ie scanf(" %[^\n]",str_name); instead of gets();
struct DVDInfo *ReadStruct( void ) {
struct DVDInfo *infoPtr;
int num;
char line[ kMaxLineLength ];
char *result;
infoPtr = malloc( sizeof( struct DVDInfo ) );
if ( NULL == infoPtr ) {
printf( "Out of memory!!! Goodbye!\n" );
exit( 0 );
}
printf( "Enter DVD Title: " );
result = fgets( line, kMaxLineLength, stdin );
line[ strlen( line ) - 1 ] = '\0';
infoPtr->title = MallocAndCopy( line );
printf( "Enter DVD comment: " );
result = fgets( line, kMaxLineLength, stdin );
line[ strlen( line ) - 1 ] = '\0';
infoPtr->comment = MallocAndCopy( line );
do {
printf( "Enter DVD Rating (1-10): " );
scanf( "%d", &num );
Flush();
}
while ( ( num < 1 ) || ( num > 10 ) );
infoPtr->rating = num;
printf( "\n----------\n" );
return( infoPtr );
}
What is the purpose of even having the variable "result" above? Nothing is done with it. The pointer returned from fgets is stored into it, but that is it, it has no purpose.
You should test that result for NULL, to check for an EOF condition or an error, instead of just ignoring it. Also, by not checking result, you are doing an strlen on line, which could have uninitialized data, because fgets failed. Really, you should have, after the fgets:
if (!result)
{
free(infoPtr); // To not leak the object allocated at the start
return NULL; // Function failed
}
You might still have leaks, if the first fgets succeeds and the second fails, because there are additional allocation to pointer members of the structure. Unfortunately, because the struct was not initialized to zero, you can't check those pointers for NULL. So, perhaps using calloc instead of malloc or at least initializing all structure pointer members to NULL, would have been a better idea.
It seems as though someone started to implement error checking, but botched it in the end. The return value should be compared with NULL, with an error reported if equal.
Most likely, the compiler threw a warning about a function return value that was ignored. The programmer didn't care about the return value of fgets and simply added in the result = to make the compiler quit nagging about it. The correct solution would be to check the return value to make sure the function completed successfully.