【问题标题】:rake task for import data from txt file ruby?从 txt 文件 ruby​​ 导入数据的 rake 任务?
【发布时间】:2016-03-12 17:47:01
【问题描述】:

我想将 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 数据库并忽略每条记录的namecity

预期结果

ImportResult Table:

id: 1
description: primary
time: 23484775884
standard: fifth
bookname: science

谢谢

【问题讨论】:

  • 您似乎有几个问题。你能把它缩小到一个吗?
  • 谢谢。我编辑了我的问题,希望这有助于理解。 @乔丹
  • 文本文件中的每一行是否总是以相同的顺序具有相同的字段?
  • 是的@jordan 它总是以相同的顺序具有相同的字段。但是有一些key:value 对类型。谢谢

标签: ruby ruby-on-rails-3 rake


【解决方案1】:

这里的主要挑战是:如何将文本文件中的一行转换为可以传递给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。

【讨论】:

  • 非常感谢,非常感谢您的解释,使您易于理解和遵循。
  • 第二个示例的 File.Open 行出现语法错误,file.each_line 是什么,file 没有在任何地方声明?
  • 我有一个Argument Error : Invalid type UTF-8 :-(
  • 我已经修复了语法错误和您提到的file 错误。至于 UTF-8 错误,我无法在没有实际数据的情况下对其进行诊断。在我的代码中,我没有使用您在问题中的line.encode ... 代码只是为了使答案更易于阅读,但您的代码中可能仍需要它。
猜你喜欢
  • 2017-05-03
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
  • 2017-09-30
  • 1970-01-01
  • 1970-01-01
  • 2013-03-04
  • 1970-01-01
相关资源
最近更新 更多