The account login system is essential in many system designs. Today, this login system has comprehensive features, including registration, login, password recovery, password modification, and password masking with * during input. It also has a complete login selection interface. Without further ado, let's get to the code!
Directory:
1. Header Files & Structs & Basic Function Implementations
2. Main Function & Login Interface
3. Registration System
4. Login System
5. Password Recovery (Change Password)
6. Password Input & Masking
7. Complete Code!!!!
First, you need to create a file named users.dat in the same directory as the .c file to store data, as shown in the image!!
You can name it something else; the .dat extension is just a random choice of mine, as long as you like it! But remember to change the filename in the code!
1. Header Files & Structs & Basic Function Implementations
// Include header files
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h> // Used to reflect unexpected situations, will be discussed below
#include <windows.h>
// Define macro constants
#define MAX_ID 11 // Maximum length of ID
#define MAX_PWD 20 // Maximum length of password
#define MAX_NAME 15 // Maximum length of name
#define MAX_SEX 5 // Maximum length of gender
#define MAX_PHONE 12 // Maximum length of phone number
// Create user structure
typedef struct Users
{
char id[MAX_ID]; // ID, i.e., the login account
char pwd[MAX_PWD]; // Password
char name[MAX_NAME]; // Name
char sex[MAX_SEX]; // Gender
char phone[MAX_PHONE]; // Phone
} Users;
// Function declarations
// Print menu
void menu();
// User registration
void Register();
// Login
void Login();
// Password recovery
void Reback();
// Set cursor position
void gotoxy();
// Get x position
int posx();
// Get y position
int posy();
// Password input (with masking functionality)
void Getpwd(char* pwd);
Here, many macro constants are defined, and you can modify the values as needed for convenience without changing them one by one in the code.
2. Main Function & Login Interface
void menu()
{
printf("************************************\n");
printf("************************************\n");
printf("*****1.Register 2.Login*****\n");
printf("*****3.Reback 0.exit *****\n");
printf("************************************\n");
printf("************************************\n");
}
int main()
{
char input;
do
{
menu();
setbuf(stdin, NULL); // Clear input buffer to avoid getchar reading leftover input
input = getchar();
switch (input)
{
case '1':
Register();
break;
case '2':
Login();
break;
case '3':
Reback();
break;
case '0':
printf("Exit successful!\n");
break;
default:
printf("Invalid selection, please try again!\n");
break;
}
} while (input != '0'); // Exit if input is 0
}
Here, a character is used as the switch case condition because if an integer is used and the input is not an integer but a character or string, the switch will enter an infinite loop. You can try it out for yourself; I won't elaborate further. Using a single character for judgment has not produced any bugs so far.
3. Registration System
void Register()
{
Users a, b; // Create temporary user structure variables, a is used to receive user input, b is used to read from the file for comparison
char tmp[20] = ""; // Used for the following judgment
printf("Welcome to the registration interface!\n");
FILE* pf = fopen("users.dat", "rb"); // "rb" means open the file in binary read-only mode
fread(&b, sizeof(Users), 1, pf); // Read data into b
if (!pf) // If opening fails
{
printf("%s", strerror(errno)); // errno can be understood as the error number, strerror can translate this number and output it on the screen
return;
}
Sleep(800);
printf("Please enter your account >>");
scanf("%s", a.id);
while (1)
{
if (strcmp(a.id, b.id)) // The two usernames are not equal
{
if (!feof(pf)) // Not at the end of the file
{
fread(&b, sizeof(Users), 1, pf); // Continue reading users from the file into b
}
else // Reached the end of the file, confirm no duplicate ID
{
break;
}
}
else // There are two equal usernames
{
printf("This username already exists, please re-enter!\n");
Sleep(1000);
fclose(pf); // Close the file
pf = NULL; // Set pf to NULL to avoid dangling pointer
return;
}
}
printf("Please enter your name >>");
scanf("%s", a.name);
printf("Please enter your gender >>");
scanf("%s", a.sex);
printf("Please enter your phone number >>");
scanf("%s", a.phone);
printf("Please enter your password >>");
Getpwd(a.pwd); // Function to input password with masking functionality, will be introduced below
printf("\nPlease confirm your password again >>");
Getpwd(tmp);
do
{
if (!strcmp(a.pwd, tmp)) // The two passwords are equal
{
pf = fopen("users.dat", "ab");
fwrite(&a, sizeof(Users), 1, pf);
printf("\nAccount registration successful, please log in!\n");
Sleep(500);
fclose(pf);
pf = NULL;
return;
}
else
{
printf("\nThe two passwords do not match! Please re-enter >>");
Getpwd(a.pwd);
printf("\nPlease confirm again >>");
Getpwd(tmp);
}
} while (1);
}
Files are opened in binary mode for writing because it looks cool!
4. Login System
void Login()
{
Users a, b; // Similarly, a is used for user input, b is used for reading from the file for comparison
FILE* pf = fopen("users.dat", "rb"); // Open the file in read-only mode
if (!pf) // If reading fails
{
printf("%s\n", strerror(errno)); // Explained above
return;
}
printf("Welcome to the login interface!\n");
Sleep(1000);
fread(&b, sizeof(Users), 1, pf); // First read a user from the file to test
printf("Please enter account >>");
scanf("%s", a.id);
while (1)
{
if (!strcmp(a.id, b.id)) // Found a matching ID in the file
{
break;
}
else
{
if (!feof(pf)) // Not at the end of the file, continue reading IDs into b
{
fread(&b, sizeof(Users), 1, pf); // Continue reading user information into b until a matching ID is found
}
else // Reached the end of the file, no matching account found
{
printf("This account does not exist! Please re-enter!\n");
Sleep(500);
fclose(pf);
pf = NULL;
return;
}
}
}
do
{
printf("Please enter password >>");
Getpwd(a.pwd); // Get password, will be discussed below
if (!strcmp(a.pwd, b.pwd)) // The entered password matches the one in the file
{
printf("\nLogin successful! Welcome!\n");
Sleep(500);
fclose(pf); // Of course, close the file after use
pf = NULL; // Set to NULL to avoid dangling pointer
return;
}
else
{
printf("\nPassword input error, please re-enter\n");
}
} while (strcmp(a.pwd, b.pwd));
}
The general idea is that the user first inputs the account (ID), then b reads the user from the file until it finds a matching ID, and then it checks if the password is correct. The library function strcmp is used to determine if the strings are the same; if they are, it returns 0, otherwise it returns a non-zero value.
5. Password Recovery (Change Password)
void Reback()
{
char tmp[20] = ""; // Used for password matching
Users a, b;
FILE* pf = fopen("users.dat", "rb+"); // "rb+" means open the file for reading and writing in binary mode
if (!pf) // As usual, first check if it can be opened successfully
{
printf("%s", strerror(errno));
return;
}
fread(&b, sizeof(Users), 1, pf); // Read one to test
printf("Please enter your account >>");
Sleep(800);
scanf("%s", a.id);
while (1) // Find a matching ID in the file
{
if (!strcmp(a.id, b.id)) // If a matching ID is found
{
break;
}
else
{
if (!feof(pf)) // Not at the end of the file, continue reading
{
fread(&b, sizeof(Users), 1, pf);
}
else
{
printf("The account you entered does not exist! Please re-enter!\n");
Sleep(500);
fclose(pf);
pf = NULL;
break;
}
}
}
// Below is information matching verification
do // Match name
{
printf("Please enter your name >>");
scanf("%s", a.name);
if (!strcmp(a.name, b.name))
{
break;
}
else
{
printf("Input error, please re-enter!\n");
}
} while (strcmp(a.name, b.name));
do // Match gender
{
printf("Please enter your gender >>");
scanf("%s", a.sex);
if (!strcmp(a.sex, b.sex))
{
break;
}
else
{
printf("Input error, please re-enter!\n");
}
} while (strcmp(a.sex, b.sex));
do // Match phone number
{
printf("Please enter your phone number >>");
scanf("%s", a.phone);
if (!strcmp(a.phone, b.phone))
{
break;
}
else
{
printf("Input error, please re-enter!\n");
}
} while (strcmp(a.phone, b.phone));
// Change password
printf("Verification successful! Please modify your password!\n");
printf("Please enter your password >>");
Getpwd(a.id);
printf("Please confirm your password again >>");
Getpwd(tmp);
if (!pf)
{
printf("%s", strerror(errno));
return;
}
// Overwrite the original password
do
{
if (!strcmp(a.pwd, tmp)) // The two passwords are equal
{
fseek(pf, -((int)(sizeof(Users) - MAX_ID)), SEEK_CUR); // Move the file stream back to the position to modify the password
fprintf(pf, "%s", a.pwd); // Overwrite the original password
printf("Password modified successfully, please log in!\n");
Sleep(500);
fclose(pf);
pf = NULL;
return;
}
else
{
printf("The two passwords do not match! Please re-enter >>");
scanf("%s", a.pwd);
printf("Please confirm again >>");
scanf("%s", tmp);
}
} while (1);
}
The general idea is to input the account -> match information -> change password. When changing the password, the file stream must be moved back to the position before the password to be modified, using fseek, -((int)(sizeof(Users) - MAX_ID)) is the range to move back.
6. Password Input & Masking
void gotoxy(int x, int y)
{
// Update cursor position
COORD pos;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); // GetStdHandle is a Windows API function.
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOutput, pos);
}
int posx()
{
CONSOLE_SCREEN_BUFFER_INFO ptr;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
return (ptr.dwCursorPosition.X);
}
int posy()
{
CONSOLE_SCREEN_BUFFER_INFO ptr;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
return (ptr.dwCursorPosition.Y);
}
void Getpwd(char* pwd)
{
int i = 0;
int x, y;
while (1)
{
pwd[i] = getch(); // Get a single password character
if (pwd[i] == VK_BACK && i >= 0) // If the input is the backspace key, VK_BACK is the keyboard key value, ASCII code value is 8
{
if (i > 0) // i>0 means something has been input, so backtrack one space
{
i--;
x = posx() - 1; // Locate x and backtrack one space
y = posy(); // Locate y
gotoxy(x, y); // Set cursor position
printf(" "); // Cover * with space
x = posx() - 1; // Backtrack again, so the cursor will display in the correct position next time
y = posy(); // Locate y
gotoxy(x, y);
continue; // Then skip this loop
}
else if (i == 0) // i==0 means nothing has been input, just skip this loop
{
continue;
}
}
if (i >= 0 && pwd[i] != VK_RETURN && pwd[i] != VK_BACK) // Something has been input
{
x = posx();
y = posy();
gotoxy(x, y);
printf("*");
}
if (i == 0 && pwd[i] == VK_RETURN) // If nothing has been input and enter is pressed, just skip this loop to avoid the program treating enter as a password
{
continue;
}
if (pwd[i] == VK_RETURN || i == MAX_PWD - 2) // Enter is pressed or the limit is reached
{
i++;
pwd[i] = '\0'; // Place '\0' at the end
break;
}
i++;
}
}
The long ones are all Windows console API functions, I won't elaborate on them here.
7. Complete Code!!!!!!!
// Include header files
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <windows.h>
// Define macro constants
#define MAX_ID 11
#define MAX_PWD 20
#define MAX_NAME 15
#define MAX_SEX 5
#define MAX_PHONE 12
// Create user structure
typedef struct Users
{
char id[MAX_ID];
char pwd[MAX_PWD];
char name[MAX_NAME];
char sex[MAX_SEX];
char phone[MAX_PHONE];
} Users;
// Function declarations
// Print menu
void menu();
// User registration
void Register();
// Login
void Login();
// Password recovery
void Reback();
// Set cursor position
void gotoxy();
// Get x position
int posx();
// Get y position
int posy();
// Password input (with masking functionality)
void Getpwd(char* pwd);
int main() // Main function
{
char input;
do
{
menu();
setbuf(stdin, NULL);
input = getchar();
switch (input)
{
case '1':
Register();
break;
case '2':
Login();
break;
case '3':
Reback();
break;
case '0':
printf("Exit successful!\n");
break;
default:
printf("Invalid selection, please try again!\n");
break;
}
} while (input != '0');
}
// Print menu
void menu()
{
printf("************************************\n");
printf("************************************\n");
printf("*****1.Register 2.Login*****\n");
printf("*****3.Reback 0.exit *****\n");
printf("************************************\n");
printf("************************************\n");
}
// Registration System
void Register()
{
Users a, b; // Create temporary user structure variables, a is used to receive user input, b is used to read from the file for comparison
char tmp[20] = ""; // Used for the following judgment
printf("Welcome to the registration interface!\n");
FILE* pf = fopen("users.dat", "rb"); // "rb" means open the file in binary read-only mode
fread(&b, sizeof(Users), 1, pf); // Read data into b
if (!pf) // If opening fails
{
printf("%s", strerror(errno)); // errno can be understood as the error number, strerror can translate this number and output it on the screen
return;
}
Sleep(800);
printf("Please enter your account >>");
scanf("%s", a.id);
while (1)
{
if (strcmp(a.id, b.id)) // The two usernames are not equal
{
if (!feof(pf)) // Not at the end of the file
{
fread(&b, sizeof(Users), 1, pf); // Continue reading users from the file into b
}
else // Reached the end of the file, confirm no duplicate ID
{
break;
}
}
else // There are two equal usernames
{
printf("This username already exists, please re-enter!\n");
Sleep(1000);
fclose(pf); // Close the file
pf = NULL; // Set pf to NULL to avoid dangling pointer
return;
}
}
printf("Please enter your name >>");
scanf("%s", a.name);
printf("Please enter your gender >>");
scanf("%s", a.sex);
printf("Please enter your phone number >>");
scanf("%s", a.phone);
printf("Please enter your password >>");
Getpwd(a.pwd); // Function to input password with masking functionality, will be introduced below
printf("\nPlease confirm your password again >>");
Getpwd(tmp);
do
{
if (!strcmp(a.pwd, tmp)) // The two passwords are equal
{
pf = fopen("users.dat", "ab");
fwrite(&a, sizeof(Users), 1, pf);
printf("\nAccount registration successful, please log in!\n");
Sleep(500);
fclose(pf);
pf = NULL;
return;
}
else
{
printf("\nThe two passwords do not match! Please re-enter >>");
Getpwd(a.pwd);
printf("\nPlease confirm again >>");
Getpwd(tmp);
}
} while (1);
}
// Login System
void Login()
{
Users a, b; // Similarly, a is used for user input, b is used for reading from the file for comparison
FILE* pf = fopen("users.dat", "rb"); // Open the file in read-only mode
if (!pf) // If reading fails
{
printf("%s\n", strerror(errno)); // Explained above
return;
}
printf("Welcome to the login interface!\n");
Sleep(1000);
fread(&b, sizeof(Users), 1, pf); // First read a user from the file to test
printf("Please enter account >>");
scanf("%s", a.id);
while (1)
{
if (!strcmp(a.id, b.id)) // Found a matching ID in the file
{
break;
}
else
{
if (!feof(pf)) // Not at the end of the file, continue reading IDs into b
{
fread(&b, sizeof(Users), 1, pf); // Continue reading user information into b until a matching ID is found
}
else // Reached the end of the file, no matching account found
{
printf("This account does not exist! Please re-enter!\n");
Sleep(500);
fclose(pf);
pf = NULL;
return;
}
}
}
do
{
printf("Please enter password >>");
Getpwd(a.pwd); // Get password, will be discussed below
if (!strcmp(a.pwd, b.pwd)) // The entered password matches the one in the file
{
printf("\nLogin successful! Welcome!\n");
Sleep(500);
fclose(pf); // Of course, close the file after use
pf = NULL; // Set to NULL to avoid dangling pointer
return;
}
else
{
printf("\nPassword input error, please re-enter\n");
}
} while (strcmp(a.pwd, b.pwd));
}
// Password Recovery
void Reback()
{
char tmp[20] = ""; // Used for password matching
Users a, b;
FILE* pf = fopen("users.dat", "rb+"); // "rb+" means open the file for reading and writing in binary mode
if (!pf) // As usual, first check if it can be opened successfully
{
printf("%s", strerror(errno));
return;
}
fread(&b, sizeof(Users), 1, pf); // Read one to test
printf("Please enter your account >>");
Sleep(800);
scanf("%s", a.id);
while (1) // Find a matching ID in the file
{
if (!strcmp(a.id, b.id)) // If a matching ID is found
{
break;
}
else
{
if (!feof(pf)) // Not at the end of the file, continue reading
{
fread(&b, sizeof(Users), 1, pf);
}
else
{
printf("The account you entered does not exist! Please re-enter!\n");
Sleep(500);
fclose(pf);
pf = NULL;
break;
}
}
}
// Below is information matching verification
do // Match name
{
printf("Please enter your name >>");
scanf("%s", a.name);
if (!strcmp(a.name, b.name))
{
break;
}
else
{
printf("Input error, please re-enter!\n");
}
} while (strcmp(a.name, b.name));
do // Match gender
{
printf("Please enter your gender >>");
scanf("%s", a.sex);
if (!strcmp(a.sex, b.sex))
{
break;
}
else
{
printf("Input error, please re-enter!\n");
}
} while (strcmp(a.sex, b.sex));
do // Match phone number
{
printf("Please enter your phone number >>");
scanf("%s", a.phone);
if (!strcmp(a.phone, b.phone))
{
break;
}
else
{
printf("Input error, please re-enter!\n");
}
} while (strcmp(a.phone, b.phone));
// Change password
printf("Verification successful! Please modify your password!\n");
printf("Please enter your password >>");
Getpwd(a.id);
printf("Please confirm your password again >>");
Getpwd(tmp);
if (!pf)
{
printf("%s", strerror(errno));
return;
}
// Overwrite the original password
do
{
if (!strcmp(a.pwd, tmp)) // The two passwords are equal
{
fseek(pf, -((int)(sizeof(Users) - MAX_ID)), SEEK_CUR); // Move the file stream back to the position to modify the password
fprintf(pf, "%s", a.pwd); // Overwrite the original password
printf("Password modified successfully, please log in!\n");
Sleep(500);
fclose(pf);
pf = NULL;
return;
}
else
{
printf("The two passwords do not match! Please re-enter >>");
scanf("%s", a.pwd);
printf("Please confirm again >>");
scanf("%s", tmp);
}
} while (1);
}
// Set cursor position
void gotoxy(int x, int y)
{
// Update cursor position
COORD pos;
HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE); // GetStdHandle is a Windows API function.
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(hOutput, pos);
}
// Get x position
int posx()
{
CONSOLE_SCREEN_BUFFER_INFO ptr;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
return (ptr.dwCursorPosition.X);
}
// Get y position
int posy()
{
CONSOLE_SCREEN_BUFFER_INFO ptr;
GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &ptr);
return (ptr.dwCursorPosition.Y);
}
// Input password
void Getpwd(char* pwd)
{
int i = 0;
int x, y;
while (1)
{
pwd[i] = getch(); // Get a single password character
if (pwd[i] == VK_BACK && i >= 0) // If the input is the backspace key, VK_BACK is the keyboard key value, ASCII code value is 8
{
if (i > 0) // i>0 means something has been input, so backtrack one space
{
i--;
x = posx() - 1; // Locate x and backtrack one space
y = posy(); // Locate y
gotoxy(x, y); // Set cursor position
printf(" "); // Cover * with space
x = posx() - 1; // Backtrack again, so the cursor will display in the correct position next time
y = posy(); // Locate y
gotoxy(x, y);
continue; // Then skip this loop
}
else if (i == 0) // i==0 means nothing has been input, just skip this loop
{
continue;
}
}
if (i >= 0 && pwd[i] != VK_RETURN && pwd[i] != VK_BACK) // Something has been input
{
x = posx();
y = posy();
gotoxy(x, y);
printf("*");
}
if (i == 0 && pwd[i] == VK_RETURN) // If nothing has been input and enter is pressed, just skip this loop to avoid the program treating enter as a password
{
continue;
}
if (pwd[i] == VK_RETURN || i == MAX_PWD - 2) // Enter is pressed or the limit is reached
{
i++;
pwd[i] = '\0'; // Place '\0' at the end
break;
}
i++;
}
}
The compiled simulation results are shown in the image below: