#python #authentication #tkinter #registration
Вопрос:
Я создаю систему входа и регистрации в tkinter. У меня есть несколько классов, поэтому я могу сделать его похожим на веб-сайт с несколькими страницами. Это выглядит так:
Итак, моя проблема в следующем: когда я регистрирую новую учетную запись, она записывает имя пользователя и пароль в файл txt. Теперь я пытаюсь заставить его выдавать ошибку, когда имя пользователя уже существует. Я попытался сделать это с помощью простого утверждения if. Это сработало в другом файле, но при работе с этими классами этого не происходит. Вот код для регистрации:
def register_user():
with open('user_data.txt', 'r ') as f:
username_info = username.get()
password_info = password.get()
for line in f:
if username_info not in line:
f.write(username_info ',')
f.write(password_info 'n')
else:
Label(text='This user already exists!').pack()
username_entry.delete(0, END)
password_entry.delete(0, END)
Может ли кто-нибудь помочь мне с этим вопросом? Заранее спасибо!
Ниже я покажу весь свой код и свой текстовый файл:
import tkinter as tk
from tkinter import *
class Page(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
def show(self):
self.lift()
class Page1(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
Label(self, text='').pack()
Label(self, text='').pack()
Label(self, text='').pack()
Label(self, text="Hello welcome to Break-Through!").pack()
class Page2(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
username = StringVar()
password = StringVar()
Label(self, text='').pack()
Label(self, text='').pack()
Label(self, text='').pack()
Label(self, text='Please enter details below').pack()
Label(self, text='').pack()
Label(self, text='Username * ').pack()
Entry(self, textvariable=username).pack()
Label(self, text='Password * ').pack()
Entry(self, textvariable=password).pack()
Label(self, text='').pack()
Button(self, text='Login', width=10, height=1).pack()
def register_user():
with open('user_data.txt', 'r ') as f:
username_info = username.get()
password_info = password.get()
for line in f:
if username_info not in line:
f.write(username_info ',')
f.write(password_info 'n')
else:
Label(text='This user already exists!').pack()
username_entry.delete(0, END)
password_entry.delete(0, END)
class Page3(Page):
def __init__(self, *args, **kwargs):
Page.__init__(self, *args, **kwargs)
global username
global password
global username_entry
global password_entry
username = StringVar()
password = StringVar()
Label(self, text='').pack()
Label(self, text='').pack()
Label(self, text='').pack()
Label(self, text='Please enter details below').pack()
Label(self, text='').pack()
Label(self, text='Username * ').pack()
username_entry = Entry(self, textvariable=username)
username_entry.pack()
Label(self, text='Password * ').pack()
password_entry = Entry(self, textvariable=password)
password_entry.pack()
Label(self, text='').pack()
Button(self, text='Register', width=10, height=1, command=register_user).pack()
class MainView(tk.Frame):
def __init__(self, *args, **kwargs):
tk.Frame.__init__(self, *args, **kwargs)
p1 = Page1(self)
p2 = Page2(self)
p3 = Page3(self)
buttonframe = tk.Frame(self)
container = tk.Frame(self)
buttonframe.pack(side="top", fill="x", expand=False)
container.pack(side="top", fill="both", expand=True)
p1.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p2.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
p3.place(in_=container, x=0, y=0, relwidth=1, relheight=1)
main_screen = tk.Button(buttonframe, text="Main-Screen", width=30, height=2, command=p1.show)
login = tk.Button(buttonframe, text="Login", width=30, height=2, command=p2.show)
register = tk.Button(buttonframe, text="Register", width=30, height=2, command=p3.show)
main_screen.pack(side='left')
login.pack(side='left')
register.pack(side='left')
p1.show()
if __name__ == "__main__":
root = tk.Tk()
main = MainView(root)
main.pack(side="top", fill="both", expand=True)
root.wm_geometry("800x800")
root.mainloop()
вот как выглядит текстовый файл с пользовательскими данными. Но он продолжает добавлять пользователей, которые уже существуют
a,a
b,b
c,c
d,d
Комментарии:
1. Не стоит слишком беспокоиться об этом прямо сейчас, но вам, вероятно, не следует сохранять пароли в виде открытого текста, хэшировать их или шифровать.
Ответ №1:
Проблема вызвана тем, как вы открываете и читаете файл. При использовании with open('user_data.txt', 'r ') as f:
вам предоставляется файловый объект, f
. Это не текстовое содержимое файла, это объект, представляющий его. Объект file также является итератором, поэтому for line in f
он будет работать, так как вы можете перебирать строки в файле, но это неправильный способ чтения файла. Проблема в том, что при использовании f.write
итератора перестает работать, поэтому программа проверяет только первую строку файла. Вместо for line in f
этого используйте for line in f.read().splitlines()
. .read()
получает текстовое содержимое из объекта file. Это все одна строка с символами новой строки. Чтобы превратить его в список строк, мы можем использовать .splitlines()
. Это превращает блок текста в список строк. Поскольку мы больше не полагаемся на f
итератор, цикл for будет просматривать каждую строку в файле.
Это вызывает вторую проблему — для каждой строки в файле вы проверяете, использовалось ли имя ранее, поэтому для каждой строки, где его нет, вы вставляете другую. Это означает, что в конечном итоге вы вставляете запись более одного раза, чего вам не нужно. Чтобы исправить это, вам нужно сначала проверить, является ли имя пользователя уникальным, а затем записать новую запись. Вот фиксированная register_user
функция:
def register_user():
with open('user_data.txt', 'r ') as f:
username_info = username.get()
password_info = password.get()
username_good = True
for line in f.read().splitlines():
if username_info == line.split(",")[0]:
username_good = False
break #Stop the for loop from continuing
if username_good:
f.write(username_info "," password_info "n")
else:
Label(text='This user already exists!').pack()
username_entry.delete(0, END)
password_entry.delete(0, END)
Я использовал username_good
, чтобы отслеживать, является ли имя пользователя уникальным или нет. Затем он проходит по строкам в файле. Для каждой строки он проверяет username_info
, равен ли line.split(",")[0]
. Это отличается от того, как вы делали это раньше, так как он разбивает строку на имя пользователя и пароль и проверяет только имя пользователя. В противном случае у пользователя не могло быть имени пользователя, которое было бы частью другого имени пользователя или пароля (например , если пользователь хотел получить имя пользователя alex
, но у кого-то оно уже было alexander
, ему бы это не разрешили, потому alex
что оно есть, alexander
даже если у них разные имена пользователей). Если он находит неуникальное имя пользователя, он устанавливает username_good
ложному и разрывает цикл, чтобы он больше не проверял строки. После завершения цикла for вы можете проверить username_good
. Если это правда, то дубликатов не найдено, поэтому вы можете записать новую строку в файл. В противном случае вы можете создать метку. Запись файла теперь должна работать по назначению.
Комментарии:
1. О, большое вам спасибо за ваш идеальный ответ!!
Ответ №2:
Если вы хорошенько подумаете о цикле for:
for line in f:
if username_info not in line:
# when username_info is not found in current line
# the file pointer will be moved to EOF due to f.write()
f.write(username_info ',')
f.write(password_info 'n')
# as the file pointer is at EOF, the for loop will be terminated
else:
Label(text='This user already exists!').pack()
Всякий username_info
раз, когда в строке не найдено, учетные данные будут добавлены в конец файла и разорвут цикл for.
Ниже приведен один из способов использования цикла for для ваших целей:
for line in f:
user, passwd = line.strip().split(',')
if username_info == user:
# better create the label once and update its text here instead
Label(text=f'User "{username_info}" already exists!').pack()
break
else:
# user not found, so register user
f.write(f'{username_info},{password_info}n')