【问题标题】:Reading raw data from an sqlite table in python从 python 中的 sqlite 表中读取原始数据
【发布时间】:2015-12-30 10:41:19
【问题描述】:

我目前正在尝试做一个登录系统,只是为了学习一些更高级的python脚本。

最初,我使用.txt 文件来保存所有信息,但结果变得混乱且不安全。

完成第一组脚本后,我决定尝试将信息保存到数据库文件中。我已经想出了如何从表中读取数据,但它周围有[(''),]。

这是一个问题,因为我需要使用原始数据与输入进行比较,而这无法通过其周围的符号来完成。

所以,我想知道是否可以将数据作为原始字符串读取,而不使用符号。我一直在主项目的单独文件中执行此操作。这是该文件的内容:

import sqlite3 as sql

con = sql.connect(r"E:\Users.db")
c = con.cursor()
con.row_factory = sql.Row
username = input("What is your username: ")

readdata = input("Select the message you wish to read: ")
c.execute("SELECT * FROM Mail WHERE (Subject LIKE '"+readdata+"') AND   (Recipient LIKE '"+username+"')")

component = c.fetchall()

for row in component:
    c.execute("SELECT Recipient FROM Mail WHERE (Subject LIKE '"+readdata+"') AND (Recipient LIKE '"+username+"')")
    rc = list(c.fetchall())
    rec = str(rc)
    c.execute("SELECT Sender FROM Mail WHERE (Subject LIKE '"+readdata+"') AND (Recipient LIKE '"+username+"')")
    snt = list(c.fetchall())
    sent = str(snt)
    c.execute("SELECT Subject FROM Mail WHERE (Subject LIKE '"+readdata+"') AND (Recipient LIKE '"+username+"')")
    sbject = list(c.fetchall())
    subject = str(sbject)
    c.execute("SELECT Message FROM Mail WHERE (Subject LIKE '"+readdata+"') AND (Recipient LIKE '"+username+"')")
    mg = list(c.fetchall())
    msg = str(mg)

    print()
    print("Sender: " + sent)
    print("Recipient: " + rec)
    print("Subject: " + subject)
    print()
    print("Message: " + msg)

这是输出:

What is your username: James
Select the message you wish to read: LOL

Sender: [('Kieran',)]
Recipient: [('James',)]
Subject: [('LOL',)]

Message: [('Hey',)]

这是所有正确的数据,但我需要删除 [(''),] 才能进行比较。我正在使用 Python 3.4.2。

【问题讨论】:

    标签: python database python-3.x sqlite


    【解决方案1】:

    您有包含所有行的列表,每行都是一系列列。如果您只希望一行,则只获取一行:

    rc = c.fetchone()
    

    现在你只有一个带有列的元组。

    您可以通过索引或使用元组赋值来获取一列:

    first_column = rc[0]
    

    或

    first_column, = rc
    

    注意这里的逗号。

    您可以将其与c.fetchone() 调用结合使用:

    first_column_of_first_row, = c.fetchone()
    

    您确实需要考虑使用 SQL 参数 以避免 SQL 注入(攻击者接管您的数据库的安全问题),并使数据库驱动程序可以在您删除查询本身的特定数据。而不是字符串连接 ('<query part>' + data + '<query part'),您应该使用 placeholders 并将引号添加到驱动程序:

    c.execute(
        "SELECT Recipient FROM Mail WHERE (Subject LIKE ?) AND (Recipient LIKE ?)",
        (readdata, username))
    

    现在readdata 和username 将被插入到您在查询中放置问号的位置,并带有适当的引号,还可以处理数据本身中引号的转义。

    对于LIKE 查询,您可能希望包含% 通配符:

    c.execute(
        "SELECT Recipient FROM Mail WHERE (Subject LIKE ?) AND (Recipient LIKE ?)",
        ('%{}%'.format(readdata), '%{}%'.format(username)))
    

    这里的str.format() 调用将% 通配符添加到开头和结尾。如果没有通配符,您不妨改用COLUMNNAME = ?,因为LIKE 在这种情况下只会匹配整个列的值。

    接下来,由于您要检索 4 列,您可以在 one 查询中获取所有 4 个列,然后在循环中提取每一行的结果:

    c.execute(
        "SELECT Recipient, Sender, Subject, Message FROM Mail WHERE (Subject LIKE ?) AND (Recipient LIKE ?)",
        ('%{}%'.format(readdata), '%{}%'.format(username)))
    
    for rec, sent, subject, msg in c:
        print("Sender: " + sent)
        print("Recipient: " + rec)
        print("Subject: " + subject)
        print()
        print("Message: " + msg)
    

    我在c 光标上循环直接;除非您需要一个随机访问所有行的列表,否则不需要 fetchall() 调用。每行正好包含 4 列,因为 SELECT 按特定顺序要求这些列,因此我们可以将它们直接分配给 4 个变量。

    【讨论】:

    • fetchone() 有什么作用? :) 我以前总是做类似a = c.fetchall() ; a.items()[0] 的事情。
    • @KevinGuan:它只获取一行,其余结果留待以后获取。
    • @KevinGuan:见cursor.fetchone() documentation。请注意,如果没有更多结果要获取,它将返回 None。
    【解决方案2】:

    fetchall 是查询返回的所有结果的迭代器。使用list 显式将此迭代器转换为其元素的列表。每个元素都是一个元组,其中包含您在查询中要求的列。

    因此,获取您想要的元素应该是 rec = rc[0][0] 或 sent = snt[0][0] 而不是 rec = str(rc) 或 sent = str(snt)。

    但是,您的查询和检索它的方式都可以改进:

    c.execute("SELECT Recipient, Sender, Subject, Message FROM Mail WHERE (Subject LIKE '"+readdata+"') AND (Recipient LIKE '"+username+"')")
    

    将在一个查询中为您提供所有四个变量,这对您的数据库更有效。

    但是请注意,使用字符串连接将参数注入查询很容易出现 SQL 漏洞。特别是如果变量的内容是用户提交的。您应该让 sqlite 驱动程序按照the docs 处理它:

    c.execute("SELECT Recipient, Sender, Subject, Message FROM Mail WHERE (Subject LIKE ?) AND (Recipient LIKE ?)", (readdata, username))
    

    使用c.fetchone() 将只从该查询中检索一个结果,而无需将其转换为列表。

    并同时将此结果影响到所有四个变量中,避免使用索引手动解包元组:

    rec, sent, subject, msg = c.fetchone()
    

    您可以通过仅使用第一个查询来进一步简化您的代码,而不必每次都执行一个新查询:

    import sqlite3 as sql
    
    con = sql.connect(r"E:\Users.db")
    c = con.cursor()
    con.row_factory = sql.Row
    username = input("What is your username: ")
    
    readdata = input("Select the message you wish to read: ")
    c.execute("SELECT Recipient, Sender, Subject, Message FROM Mail WHERE (Subject LIKE ?) AND   (Recipient LIKE ?)", (readdata, username))
    
    for rec, sent, subject, msg in c.fetchall():
        print()
        print("Sender: " + sent)
        print("Recipient: " + rec)
        print("Subject: " + subject)
        print()
        print("Message: " + msg)
    

    【讨论】:

      【解决方案3】:

      您所做的很糟糕:您执行 4 次相同的请求以获取 4 个属性,并且您正在将输入数据连接到可能导致 sql injection 的查询字符串中。

      你应该重写它:

      import sqlite3 as sql
      
      con = sql.connect(r"E:\Users.db")
      c = con.cursor()
      con.row_factory = sql.Row
      username = input("What is your username: ")
      
      readdata = input("Select the message you wish to read: ")
      c.execute("SELECT Sender, Recipient, Subject, Message FROM Mail WHERE (Subject LIKE ?) AND   (Recipient LIKE ?)", (readdata, username))
      
      component = c.fetchall()
      
      for row in component:
          (sent, rec, subject, msg) = tuple(row)
          print()
          print("Sender: " + sent)
          print("Recipient: " + rec)
          print("Subject: " + subject)
          print()
          print("Message: " + msg)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-03-07
        • 2012-02-11
        • 2019-07-25
        • 2012-07-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多