C语言登录验证:输入用户名与文件存储内容匹配失败问题排查
问题原因分析
以下是导致用户名匹配失败的核心问题:
- 换行符干扰:
create()函数用fprintf(fptr, "%s\n", username)写入用户名时,会在末尾添加换行符\n;而usernamef()里用scanf("%s", username)读取输入时,不会包含换行符。strcmp对比时,输入的用户名(如"alex")和文件中读取的内容(如"alex\n")自然不相等。 - strcmp语法错误:代码中写的是
if(0 = strcmp(username, theString)),这是赋值操作而非比较,应该写成if(strcmp(username, theString) == 0)。 - 缓冲区长度不匹配+溢出风险:
usernamef()里定义的username[5]和theString[5]长度太小,而create()允许用户名最多9个字符(数组username[10]),会导致读取时截断;同时scanf("%s")未限制长度,存在缓冲区溢出风险。 - 文件操作不规范:
usernamef()中fopen后未检查文件是否成功打开,读取完成后也未关闭文件;全局FILE *fptr容易引发资源冲突。 - passwordf逻辑顺序错误:先执行
strcmp(password, passwordn)再读取密码,此时passwordn是未初始化的垃圾值,逻辑完全颠倒。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <string.h> // 提前声明函数 void usernamef(); void passwordf(const char *correct_pwd); void shade(); void create(); int main() { char a; printf("Welcome! Do you have an account ?\n"); printf("\tType \"y\" for yes or type \"n\" for no.\n"); // 吃掉输入残留的换行符,避免读取y/n时出错 getchar(); scanf("%c", &a); if(a == 'y' || a == 'Y'){ usernamef(); } else { create(); } return 0; } void usernamef() { // 匹配create函数的长度,留足'\0'和处理换行的空间 char username[10]; char theString[11]; // 多1位存储换行符 FILE *fptr = fopen("accounts.txt", "r"); if(!fptr){ printf("Failed to open accounts file!\n"); return; } printf("Enter your username\n"); // 限制输入长度,避免缓冲区溢出 scanf("%9s", username); // 逐行读取文件内容 while(fgets(theString, sizeof(theString), fptr)) { // 去掉换行符(如果存在) size_t len = strlen(theString); if(len > 0 && theString[len-1] == '\n'){ theString[len-1] = '\0'; } // 正确的strcmp比较逻辑 if(strcmp(username, theString) == 0) { printf("Username matched!\n"); // 读取对应密码(创建时用户名和密码各占一行) fgets(theString, sizeof(theString), fptr); len = strlen(theString); if(len > 0 && theString[len-1] == '\n'){ theString[len-1] = '\0'; } passwordf(theString); fclose(fptr); return; } // 跳过密码行,继续查找下一个用户名 fgets(theString, sizeof(theString), fptr); } printf("Username not found!\n"); fclose(fptr); } void passwordf(const char *correct_pwd) { char passwordn[10]; printf("Enter your password\n"); scanf("%9s", passwordn); if(strcmp(correct_pwd, passwordn) == 0){ shade(); } else { printf("Password incorrect, try again or type r to create an account\n"); } } void shade() { printf("Welcome back! Hope you're having a good day :)\n"); } void create(){ char username[10]; char password[10]; printf("Create an account to continue\n"); printf("Make sure to follow these instructions\n"); printf("\t1. Username and password must be shorter than 10 characters.\n"); printf("\t2. No spaces, use a single word.\n"); printf("\t3. No special characters.\n"); printf("Now enter the username: "); scanf("%9s", username); FILE *fptr = fopen("accounts.txt", "a"); if(!fptr){ printf("Failed to open accounts file!\n"); return; } fprintf(fptr, "%s\n", username); fclose(fptr); printf("Enter the password: "); scanf("%9s", password); fptr = fopen("accounts.txt", "a"); if(!fptr){ printf("Failed to open accounts file!\n"); return; } fprintf(fptr, "%s\n", password); fclose(fptr); shade(); }
关键修改说明
- 处理换行符:读取文件行后,检查末尾是否有
\n,如果有就替换为\0,确保和输入的用户名格式一致。 - 修正strcmp语法:把赋值操作改为比较操作,保证逻辑正确。
- 缓冲区安全:定义足够长度的数组,
scanf用%9s限制输入长度,避免缓冲区溢出。 - 规范文件操作:使用局部
FILE指针,每次打开后检查是否成功,用完及时关闭,避免资源泄漏。 - 调整passwordf逻辑:先读取用户输入的密码,再和正确密码对比,同时传递正确密码作为参数,修正逻辑顺序。
- 修复输入残留问题:
main里用getchar()吃掉输入残留的换行符,避免读取y/n时出错。
内容的提问来源于stack exchange,提问作者Alaa Younsi
相关产品推荐
相关产品推荐

