【问题标题】:(Long) Removing Single Quotes From Strings in a List(长)从列表中的字符串中删除单引号
【发布时间】:2016-12-08 04:40:36
【问题描述】:

这有点模糊,因为该程序相当深入,但请坚持我,因为我会尽力解释它。我编写了一个程序,它接受一个.csv 文件并将其转换为MySQL 数据库的INSERT INTO 语句。例如:

ID   Number   Letter   Decimal   Random
0    1        a        1.8       A9B34
1    4        b        2.4       C8J91
2    7        c        3.7       L9O77

会产生如下插入语句:

INSERT INTO table_name ('ID' int, 'Number' int, 'Letter' varchar(). 'Decimal', float(), 'Random' varchar()) VALUES ('0', '1', 'a', '1.8', 'A9B34');

但是,并非所有.csv 文件都具有相同的列标题,但它们需要插入到同一个表中。对于没有某些列标题的文件,我想插入一个 NULL 值来显示这一点。例如:

假设第一个 .csv 文件 A 包含以下信息:

ID   Number   Decimal   Random
0    1        1.8       A9B34
1    4        2.4       C8J91

第二个.csv文件B有不同的列标题:

ID   Number   Letter   Decimal
0    3        x        5.6
1    8        y        4.8

在转换为INSERT 语句并放入数据库后,理想情况下如下所示:

ID   TableID   Number   Decimal   Letter   Random
0    A         1        1.8       NULL     A9B34
1    A         4        2.4       NULL     C8J91
2    B         3        5.6       x        NULL
3    B         8        4.8       y        NULL

现在我可能会开始失去你。

为了完成我需要的工作,我首先获取每个文件并创建 所有列标题的主列表,.csv 文件:

def createMaster(path):
    global master
    master = []
    for file in os.listdir(path):
        if file.endswith('.csv'):
            with open(path + file) as inFile:
                csvFile = csv.reader(inFile)
                col = next(csvFile) # gets the first line of the file, aka the column headers
                master.extend(col) # adds the column headers from each file to the master list
                masterTemp = OrderedDict.fromkeys(master) # gets rid of duplicates while maintaining order
                masterFinal = list(masterTemp.keys()) # turns from OrderedDict to list
    return masterFinal

这将从多个 .csv 文件中获取所有列标题并将它们按顺序组装到一个主列表中,而不会重复:

['ID', 'Number', 'Decimal', 'Letter', 'Random']

这为我提供了INSERT 语句的第一部分。现在我需要将VALUES 部分添加到语句中,因此我一次列出每个.csv 文件的每一行中的所有值。为每一行创建一个临时列表,然后将该文件的列标题列表与所有文件的列标题主列表进行比较。然后它遍历主列表中的每个事物并尝试获取列列表中相同项目的索引。如果它在列列表中找到该项目,它会将同一索引处的行列表中的项目插入到临时列表中。如果它找不到项目,它会将'NULL' 插入到临时列表中。一旦它完成了临时列表,它就会将列表转换为正确的 MySQL 语法的字符串,并将其附加到 .sql 文件以进行插入。以下是代码中的相同想法:

def createInsert(inPath, outPath):
    for file in os.listdir(inpath):
        if file.endswith('.csv'):
            with open(inPath + file) as inFile:
                with open(outPath + 'table_name' + '.sql', 'a') as outFile:
                    csvFile = csv.reader(inFile)
                    col = next(csvFile) # gets the first row of column headers
                    for row in csvFile:
                        tempMaster = [] # creates a tempMaster list
                        insert = 'INSERT INTO ' + 'table_name' + ' (' + ','.join(master)+ ') VALUES ' # SQL syntax crap
                        for x in master:
                            try:
                                i = col.index(x) # looks for the value in the column list
                                r = row[i] # gets the row value at the same index as the found column
                                tempMaster.append(r) # appends the row value to a temporary list
                            except ValueError:
                                tempMaster.append('NULL') # if the value is not found in the column list it just appends the string to the row master list
                            values = map((lambda x: "'" + x.strip() + "'"), tempMaster) # converts tempMaster from a list to a string
                            printOut = insert + ' (' + ','.join(values) + '):')
                            outFile.write(printOut + '\n') # writes the insert statement to the file

终于到了提问时间了。

这个程序的问题是createInsert() 从 tempMaster 列表中获取所有行值,并通过行将它们与' 标记连接:

values = map((lambda x: "'" + x.strip() + "'"), tempMaster)

这一切都很好除了 MySQL希望插入NULL值并且只是NULL而不是'NULL'

如何获取组装的行列表并搜索 'NULL' 字符串并将它们更改为 NULL

我有两种不同的想法:

我可以按照这些思路做一些事情,从' 标记中拉出NULL 字符串并将其替换到列表中。

def findBetween(s, first, last):
    try:
        start = s.index(first) + len(first)
        end = s.index(last, start)
        return s[start:end]
    except ValueError:
        print('ERROR: findBetween function failure.')

def removeNull(aList):
    tempList = []
    for x in aList:
        if x == 'NULL':
            norm = findBetween(x, "'", "'")
            tempList.append(norm)
        else:
            tempList.append(x)
    return tempList

或者,我可以将 NULL 值添加到列表中,而无需以 ' 开头。 这是在createInsert() 函数中。

for x in tempMaster:
    if x == 'NULL':
        value = x
        tempMaster.append(value)
    else:
        value = "'" + x + "'"
        tempMaster.append(value)
values = map((lambda x: x.strip()), tempMaster)
printOut = insert + ' (' + ','.join(values) + ');')
outFile.write(printOut + '\n')

但是我认为这些都不可行,因为它们会显着减慢程序的速度(对于较大的文件,这些会引发MemoryError)。所以我问你的意见。如果这令人困惑或难以理解,我深表歉意。如果是这种情况,请告诉我我可以解决哪些问题以便更容易理解,并祝贺您完成了!

【问题讨论】:

  • 你有没有试过看看它们有多慢?
  • 是的,我有。它可以运行一些较小的.csv 文件,但在处理一些较大的文件时会生成MemoryError
  • 告诉我,如果这是在以下列之一中,您的数据库会发生什么:" DROP TABLE table_name;"?这种插入数据的方法使您容易受到 SQL 注入攻击。永远不要向 SQL 查询添加任意输入。你应该考虑使用 SQLAlchemy 之类的东西来帮助你隔离。
  • @IanAuld 如果我决定进一步实施这个程序,这绝对是下一步。但是,现在这只是将旧数据(我知道是安全的)插入新数据库的临时方法。我将查看您的评论以及 SQLAlchemy,以帮助避免将来出现这些潜在问题。感谢您的建议!

标签: python mysql python-3.x csv


【解决方案1】:

而不是

values = map((lambda x: "'" + x.strip() + "'"), tempMaster)

放这个

 values = map((lambda x: "'" + x.strip() + "'" if x!='NULL' else x), tempMaster)

编辑

感谢您接受/支持我的简单技巧,但我不确定这是最优的。 在更全局的范围内,您本可以避免使用这种 map/lambda 的东西(除非我遗漏了一些东西)。
                for row in csvFile:
                    values = [] # creates the final list
                    insert = 'INSERT INTO ' + 'table_name' + ' (' + ','.join(master)+ ') VALUES ' # SQL syntax crap
                    for x in master:
                        try:
                            i = col.index(x) # looks for the value in the column list
                            r = row[i] # gets the row value at the same index as the found column
                            value.append("'"+r.strip()+"'") # appends the row value to the final list
                        except ValueError:
                            value.append('NULL') # if the value is not found in the column list it just appends the string to the row master list

那么你有value 正确填充,节省内存和CPU。

【讨论】:

  • 我在问题中没有提到的部分程序中犯了一个错误,但是一旦我修复它,它就完美地工作了。谢谢!
  • 检查我的最后一次编辑,也许我们可以做得更好、更简单(好吧,我们不会使用maplambda,这看起来不那么令人印象深刻,这是缺点:))
【解决方案2】:

我检查了您的要求,发现您的目录中有多个 CSV。这些 csv 具有动态列。我的方法是创建所有列的静态列表

staticColumnList = ["ID","TableID","Number","Decimal","Letter","Random"]

现在在读取文件时,获取标题行并为相应列的元组创建一个临时列表,例如

[(ID, column no in csv), (TableID, 'A' - File Name), (Number, column no in csv) etc...]

如果您在 csv 中没有列,则将 x 放入对应关系中,例如 ("Letter", x)。现在每行循环并分配或选择这样的值:-

wholeDataList = []
rowList = []
for column in staticColumnList:
    if int of type(column[1]):
      rowList.append("'"+str(rowCSV[column[1]])+"'")
    elif 'X' == column[1]:
      rowList.append('null')
    else:
      rowList.append("'"+column[1]+"'")


wholeDataList.append("("+",".join(rowList)+")")

你终于有了精心准备的语句,像这样:-

qry = "INSERT into .. ("+",".join(staticColumnList)+") values " + ",".join(wholeDataList)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 1970-01-01
    • 2023-04-04
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多