为Ruby SQLite插入转义字符串

mic*_*ael 6 ruby sqlite sqlite3-ruby

我正在创建一个Ruby脚本,将大约150k行的制表符分隔文本文件导入SQLite.到目前为止:

require 'sqlite3'

file = File.new("/Users/michael/catalog.txt")
string = []

# Escape single quotes, remove newline, split on tabs, 
# wrap each item in quotes, and join with commas
def prepare_for_insert(s)
  s.gsub(/'/,"\\\\'").chomp.split(/\t/).map {|str| "'#{str}'"}.join(", ")
end

file.each_line do |line|
  string << prepare_for_insert(line)
end

database = SQLite3::Database.new("/Users/michael/catalog.db")

# Insert each string into the database
string.each do |str|
  database.execute( "INSERT INTO CATALOG VALUES (#{str})")
end
Run Code Online (Sandbox Code Playgroud)

尽管gsub在我的prepare_for_insert方法中要转义单引号,但脚本在包含单引号的第一行出错:

/Users/michael/.rvm/gems/ruby-1.9.3-p0/gems/sqlite3-1.3.5/lib/sqlite3/database.rb:91:
in `initialize': near "s": syntax error (SQLite3::SQLException)
Run Code Online (Sandbox Code Playgroud)

这是第15行的错误.如果我检查该行puts string[14],我可以看到它在"s"附近显示错误的位置.它看起来像这样:'Touch the Top of the World: A Blind Man\'s Journey to Climb Farther Than the Eye Can See'

看起来单引号被转义,为什么我仍然得到错误?

mu *_*ort 12

不要那样做,字符串插值和SQL往往是一个糟糕的组合.使用预准备语句,让驱动程序处理引用和转义:

# Ditch the gsub in prepare_for_insert and...
db  = SQLite3::Database.new('/Users/michael/catalog.db')
ins = db.prepare('insert into catalog (column_name) values (?)')
string.each { |s| ins.execute(s) }
Run Code Online (Sandbox Code Playgroud)

你当然应该column_name用真正的列名替换; 您不必在INSERT中指定列名,但无论如何都应该始终执行.如果需要插入更多列,则添加更多占位符和参数ins.execute.

使用prepare并且execute应该更快,更安全,更容易,并且它不会让你觉得你在1999年编写PHP.

此外,您应该使用标准的CSV解析器来解析与制表符分隔的文件,XSV格式处理起来并不是很有趣(事实上它们是完全邪恶的)并且您有更好的时间来处理他们的时间而不是处理他们的废话和边缘情况,什么不是.

  • @michaelmichael:是的,还有34个`ins.execute`的参数,但是你可以将参数放在一个数组中并将它们保存在`ins.execute(*array)中,以防止愚蠢.您也可以使用`(['?']*34).join(',')`来构建占位符(这是可以的,因为您确切知道您正在使用哪些字符串,尝试在SQL中使用未知字符串只是虽然要求麻烦). (3认同)