6

約15万行のタブ区切りのテキストファイルをSQLiteにインポートするRubyスクリプトを作成しています。ここまでです:

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

私の方法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)

15 行目でエラーが発生しています。その行を で検査するとputs string[14]、「s」の近くでエラーが表示されている場所がわかります。次のようになります。'Touch the Top of the World: A Blind Man\'s Journey to Climb Farther Than the Eye Can See'

一重引用符がエスケープされているように見えますが、それでもエラーが発生するのはなぜですか?

4

1 に答える 1

13

文字列補間と 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) }

column_nameもちろん、実際の列名に置き換える必要があります。INSERT で列名を指定する必要はありませんが、常に指定する必要があります。さらに列を挿入する必要がある場合は、プレースホルダーと引数を に追加しますins.execute

prepareandを使用するとexecute、より速く、より安全で、より簡単になるはずであり、1999 年に PHP を書いているような気分にはなりません。

また、標準の CSV パーサーを使用してタブ区切りファイルを解析する必要があります。XSV 形式を扱うのはあまり楽しいものではありません (実際にはまったく悪です)。ナンセンスとエッジケースとそうでないもの。

于 2012-03-08T07:42:02.217 に答える