从 txt 文件 ruby 导入数据的 rake 任务?
rake task for import data from txt file ruby?
我想将数据从 txt 文件导入 Ruby 中的数据库。我试图为此创建一个 rake 任务,并努力寻找一种优雅的方法。
到目前为止我的 Rake 任务:
desc "Import schools."
task :import_schools => :environment do
File.open(File.join(Rails.root, "imports", "schools.txt"), "r").each do |line|
if ! line.valid_encoding?
s = line.encode("UTF-16be", :invalid=>:replace, :replace=>"?").encode('UTF-8')
s.gsub(/dr/i,'med')
description, time, standards, books, choices = s.strip.split("\t")
u = ImportResult.new(:description => description, :time => time)
u.save
end
end
end
我的 txt 文件数据如下:
primary 23484775884 standard:fifth book:science choice:maths name:Joseph city:London
secondary 46537728836 standard:fourth book:english choice:maths name:Jain city:Manchester
.........
我想将每条记录插入 ImportResult
数据库并忽略每条记录的 name
和 city
。
预期结果
ImportResult Table:
id: 1
description: primary
time: 23484775884
standard: fifth
bookname: science
谢谢
这里的主要挑战是:如何将文本文件中的一行转换为可以传递给 MyModel.create
的属性散列?由于文本文件中的每一行都有相同顺序的字段,一种简单的方法是只使用正则表达式:
LINE_TO_ATTRS_EXPR = /
\A
(?<description>\w+)\s+
(?<time>\d+)\s+
standard:(?<standard>\w+)\s+
book:(?<book>\w+)\s+
choice:(?<choice>\w+)\s
/x
def line_to_attrs(line)
matches = LINE_TO_ATTRS_EXPR.match(line)
Hash[ matches.names.zip(matches.captures) ]
end
p line_to_attrs("primary 23484775884 standard:fifth book:science choice:maths name:Joseph city:London")
# => { "description" => "primary",
# "time" => "23484775884",
# "standard" => "fifth",
# "book" => "science",
# "choice" => "maths" }
这里我假设 time
字段将始终是一串数字 (\d+
) 并且字段由空格分隔 (\s+
)。
另一种方法是在空白处拆分行,然后在冒号 (:
) 上拆分每个部分,并使用左侧的部分作为键,右侧的部分作为值.由于前两个字段的格式不同,我们首先将它们从数组中取出。然后我们可以使用 each_with_object
将它们放入一个 Hash 中,跳过我们不想要的键:
def line_to_attrs(line)
attrs = {}
attrs[:description], attrs[:time], *rest = line.split(/\s+/)
rest.each_with_object(attrs) do |part, attrs|
key, val = part.split(':')
next if key =~ /^(name|city)$/
attrs[key.to_sym] = val
end
end
无论您选择哪种方法,您现在都可以将其应用于每一行以获取要传递给 ImportResult.create!
:
的属性散列
File.open(Rails.root + "imports/schools.txt", "r") do |file|
ImportResult.transaction do
file.each_line do |line|
ImportResult.create!(line_to_attrs(line))
end
end
end
请注意,我使用 File.open(...) do ...
而不是 File.open(...).each
做。将 open
与块一起使用可确保在操作完成时关闭文件,即使发生错误也是如此。
但是,如果您的输入很大,您可能会发现这很慢。那是因为您要为每一行创建一个 ActiveRecord 对象并一次执行一个插入。在交易中这样做有帮助,但仅此而已。如果性能成为问题,我建议查看 activerecord-import gem.
我想将数据从 txt 文件导入 Ruby 中的数据库。我试图为此创建一个 rake 任务,并努力寻找一种优雅的方法。
到目前为止我的 Rake 任务:
desc "Import schools."
task :import_schools => :environment do
File.open(File.join(Rails.root, "imports", "schools.txt"), "r").each do |line|
if ! line.valid_encoding?
s = line.encode("UTF-16be", :invalid=>:replace, :replace=>"?").encode('UTF-8')
s.gsub(/dr/i,'med')
description, time, standards, books, choices = s.strip.split("\t")
u = ImportResult.new(:description => description, :time => time)
u.save
end
end
end
我的 txt 文件数据如下:
primary 23484775884 standard:fifth book:science choice:maths name:Joseph city:London
secondary 46537728836 standard:fourth book:english choice:maths name:Jain city:Manchester
.........
我想将每条记录插入 ImportResult
数据库并忽略每条记录的 name
和 city
。
预期结果
ImportResult Table:
id: 1
description: primary
time: 23484775884
standard: fifth
bookname: science
谢谢
这里的主要挑战是:如何将文本文件中的一行转换为可以传递给 MyModel.create
的属性散列?由于文本文件中的每一行都有相同顺序的字段,一种简单的方法是只使用正则表达式:
LINE_TO_ATTRS_EXPR = /
\A
(?<description>\w+)\s+
(?<time>\d+)\s+
standard:(?<standard>\w+)\s+
book:(?<book>\w+)\s+
choice:(?<choice>\w+)\s
/x
def line_to_attrs(line)
matches = LINE_TO_ATTRS_EXPR.match(line)
Hash[ matches.names.zip(matches.captures) ]
end
p line_to_attrs("primary 23484775884 standard:fifth book:science choice:maths name:Joseph city:London")
# => { "description" => "primary",
# "time" => "23484775884",
# "standard" => "fifth",
# "book" => "science",
# "choice" => "maths" }
这里我假设 time
字段将始终是一串数字 (\d+
) 并且字段由空格分隔 (\s+
)。
另一种方法是在空白处拆分行,然后在冒号 (:
) 上拆分每个部分,并使用左侧的部分作为键,右侧的部分作为值.由于前两个字段的格式不同,我们首先将它们从数组中取出。然后我们可以使用 each_with_object
将它们放入一个 Hash 中,跳过我们不想要的键:
def line_to_attrs(line)
attrs = {}
attrs[:description], attrs[:time], *rest = line.split(/\s+/)
rest.each_with_object(attrs) do |part, attrs|
key, val = part.split(':')
next if key =~ /^(name|city)$/
attrs[key.to_sym] = val
end
end
无论您选择哪种方法,您现在都可以将其应用于每一行以获取要传递给 ImportResult.create!
:
File.open(Rails.root + "imports/schools.txt", "r") do |file|
ImportResult.transaction do
file.each_line do |line|
ImportResult.create!(line_to_attrs(line))
end
end
end
请注意,我使用 File.open(...) do ...
而不是 File.open(...).each
做。将 open
与块一起使用可确保在操作完成时关闭文件,即使发生错误也是如此。
但是,如果您的输入很大,您可能会发现这很慢。那是因为您要为每一行创建一个 ActiveRecord 对象并一次执行一个插入。在交易中这样做有帮助,但仅此而已。如果性能成为问题,我建议查看 activerecord-import gem.