Python 和文本文件中的 Pickle 模块

Pickle module in Python and text files

我最近提出了一个问题并得到了一个答案,我必须 'pickle' 我的代码。作为初学者,我不知道该怎么做。

这是您使用 pickle 创建登录系统的方式,但是我不推荐这种方式,因为它有很多安全性 issue.I 更愿意将 python 连接到 SQL 服务器,并且将密码存储在数据库中。

import pickle

def register(username,password):
    user_details = dict()
    with open("users.txt",'rb') as f:
        user_details = pickle.load(f)
    if username in user_details:
        print "User already exsits"
    else:
        user_details[username] = password
        print "User added successfully"
    with open("users.txt",'wb+') as f:
        pickle.dump(user_details,f)

def login(username,password):
    user_details = dict()
    with open("users.txt",'rb') as f:
        user_details = pickle.load(f)
    if username in user_details:
        if user_details[username] == password:
            print "Correct Login successful"
        else:
            print "Incorrect Details"

def init():
    user_details = dict()
    with open("users.txt",'wb+') as f:
        pickle.dump(user_details,f)

init()
register("s","s")
login("s","s")

要初始化调用 init() 函数。

所以,如果我通过查看你提出的与此相关的其他两个问题 (1 and 2) 理解正确,那么你的问题有两个部分:

一个正在生成一个包含 user/passwords 列表的文件,第二个正在使用该文件做一个 "login" 系统。

写入文件的问题在于,您可以将文件视为文本...它并没有真正保留 Python list 所以你需要找到一种方法将你喜欢的 users 列表转换为文本,然后再转换回 list 列表,这样你就可以实际使用它了。

有许多预制的序列化格式。这里有一些:JSON, CSV, YAML, or the one another user recommended in another question, Pickle

既然您在 another post 中提到您将其用于学习目的,那么让我们尽量保持简单,好吗?

让我们将练习分成两个 python 文件:一个只生成密码文件,另一个尝试读取和验证用户输入的 username/password。

脚本 1:生成密码文件。

所以...您有 list 对 username/password,您必须将其转换为文本,以便将其存储在文件。让我们浏览列表中的每个条目并将其写入文件。借鉴一下 Linux 的灵​​感,在文件的每一行使用分号字符 (;) 来标记用户名和密码之间的分隔,怎么样?像这样:

sample_users = [
    ["user1", "password1"],
    ["user2", "password2"],
    ["user3", "password3"]
]
users_file = open("./users.txt", "w")
for sample_user in sample_users:
    username = sample_user[0]
    password = sample_user[1]
    users_file.write(username + ';' + password + '\n')
users_file.close()

将其放入 .py 文件并 运行 。它应该在脚本所在的同一目录中生成一个名为 users.txt 的文件。我建议您查看该文件(任何文本编辑器都可以)。你会看到它看起来像这样:

user1;password1
user2;password2
user3;password3

顺便说一下,利用 Python 的 context managers 提供的 "autoclosing" 功能是一个更好的练习。您可以将该脚本编写为:

with open("./users.txt", "w") as users_file:
    for sample_user in sample_users:
        username = sample_user[0]
        password = sample_user[1]
        users_file.write(username + ';' + password + '\n')

看到了吗?无需致电 .close()。如果在执行 运行 代码时发生某些事情,您可以放心,您的文件在离开 with 块后关闭(当解释器到达 with 块的末尾时,调用文件的特殊功能__exit__会自动运行,文件会被关闭)


脚本 2:使用密码文件

好的...所以我们有一个文件,每行都有 username;password\n。让我们使用它。

对于这一部分,您需要了解 str 对象的 split (to separate username and password using the semicolon) and rstrip(删除末尾的换行符 \n)方法的作用。

我们需要 "rebuild" 两个变量(usernamepassword)来自形状为 username;password\n 的文本行。然后查看是否在文件中找到用户名,如果是,提示用户输入密码(并验证密码是否正确):

def loginFunction():
    userEntry = ""
    foundName = False

    while userEntry == "":
        userEntry = raw_input("Enter your username: ")
        usersFile = open("users.txt", "r")
        for line in usersFile:
            print("This is the line I read:%s", line,)
            # Read line by line instead of loading the whole file into memory
            # In this case, it won't matter, but it's a good practice to avoid
            # running out of memory if you have reaaaally large files
            line_without_newline = line.rstrip('\n')       # Remove ending \n
            user_record = line_without_newline.split(';')  # Separate into username and password (make the line a list again)
            userName = user_record[0]
            password = user_record[1]
            # Might as well do userName, password = line_without_newline.split(';')
            if userName == userEntry:
                foundName = True
                print("User found. Verifying password.")
                passwordEntry = raw_input("Enter your password: ")
                if passwordEntry == password:
                    print "Username and passwords are correct"
                    break
                else:
                    print "incorrect"
                    userEntry = ""

        if not foundName:
            print("Username not recognised")
            userEntry = ""


if __name__ == "__main__":
    loginFunction()

我相信这应该可以满足您的需求?如果您还有其他问题,请在答案中发表评论。

祝你编码愉快!


PS:所以...泡菜怎么样?

Pickle 是一个以 "safer" 和更自动化的方式将 Python 对象序列化到文件中的模块。如果您想使用它,方法如下(至少是一种方式):

  1. 正在生成密码文件:

    import pickle
    
    sample_users = [
        ["user1", "password1"],
        ["user2", "password2"],
        ["user3", "password3"]
     ]
    
     with open('./users.txt', 'w') as f:
         pickler = pickle.Pickler(f)
         for sample_user in sample_users:
            pickler.dump(sample_user)
    

    和以前一样,此时我建议您使用常规文本编辑器查看文件 users.txt 的外观。您会发现它与之前的文件(用户名和密码用分号分隔的文件)有很大不同。是这样的:

        (lp0
        S'user1'
        p1
        aS'password1'
        p2
        a.(lp3
        S'user2'
        p4
        aS'password2'
        p5
        a.(lp6
        S'user3'
        p7
        aS'password3'
        p8
        a.%
    
  2. 使用文件:

    import pickle
    
    def loginFunction():
        userEntry = ""
    
        while userEntry == "":
            userEntry = raw_input("Enter your username: ")
            usersFile = open("users.txt", "r")
            unpickler = pickle.Unpickler(usersFile)
            while True:
                try:
                    user_record = unpickler.load()
                    userName = user_record[0]
                    password = user_record[1]
                    if userName == userEntry:
                        print("User found. Verifying password.")
                        passwordEntry = raw_input("Enter your password: ")
                        if passwordEntry == password:
                            print "Username and passwords are correct"
                        else:
                            print "incorrect"
                            userEntry = ""
                        # Watch out for the indentation here!!
                        break  # Break anyway if the username has been found
    
    
                except EOFError:
                    # Oh oh... the call to `unpickler.load` broke 
                    # because we reached the end of the file and
                    # there's nothing else to load...
                    print("Username not recognised")
                    userEntry = ""
                    break
    
    
    if __name__ == "__main__":
        loginFunction()
    

如果您意识到,当您执行 user_record = unpickler.load() 时,您已经在 user_record 中获得了 2 个项目 Python list ] 多变的。您无需将文本转换为列表:unpickler 已经为您完成了。这要归功于 picker.dump 存储到文件中的所有 "extra" 信息,它允许 unpickler 到 "know" 需要返回的对象是一个列表。