如何生成JSON数据增量?

时间:2014-04-08 07:31:29

标签: ruby json

我有一个大约1000000行,300 + Mb的数据文件。我想将其转换为JSON文件。

JSON.generateHash.merge可以帮助生成JSON,但我需要等待程序生成一个完整的哈希值。这花费了太多时间 我想逐步将JSON写入文件。

这是我的代码:

require 'yajl/json_gem'
my_hash = {}
fd1 = File.open("foo.json", "w")
fd2 = File.open("foo.log")
fd2.each_line do | line |
  l = fd2.lineno
  remote_addr = line.split(" ")[0]
  time_local = line.split("]")[0].split("[")[1]
  item = {l => {:remote_addr => remote_addr, :time_local => time_local}}
  # {
  #   1: {
  #     remote_addr: "1.2.3.4",
  #     time_local: ""
  #   }, 
  #   2: {
  #     ...
  #   },
  #   ...
  # }
  my_hash.merge!(item)
end
fd2.close
fd1.puts JSON.generate(my_hash)
fd1.close

这是我的数据:

// access.log
1.2.3.4 - - [02/Apr/2014:03:23:06 +0800] "GET /index" 200 1 "http://foo" "Mozilla/5.0" "-"

有什么想法吗?提前谢谢。

-

编辑:

实际上,我最好转换原始数据:

[
  {
     id: "1",
     remote_addr: "1.2.3.4",
     time_local: "02/Apr/2014:03:23:06 +0800"
  }, 
  {
   ...
  },
  {
  ...
  }
]

2 个答案:

答案 0 :(得分:1)

这是基于样本行生成有效JSON输出的基本代码:

require 'json'


lines = [
  '1.2.3.4 - - [02/Apr/2014:03:23:06 +0800] "GET /index" 200 1 "http://foo" "Mozilla/5.0" "-"',
  '1.2.3.5 - - [03/Apr/2014:03:23:06 +0800] "GET /index" 200 1 "http://foo" "Mozilla/5.0" "-"'
]

lines.each_with_index do |line, l|
  puts '{' if l == 0
  remote_addr, time_local = /^(\S+) .+ \[(.+)\]/.match(line).captures
  print '"%d":{"remote_addr":"%s","time_local":"%s"}' % [l + 1, remote_addr, time_local]
  puts ',' if l + 1 < lines.size
end
puts "\n}"

# >> {
# >> "1":{"remote_addr":"1.2.3.4","time_local":"02/Apr/2014:03:23:06 +0800"},
# >> "2":{"remote_addr":"1.2.3.5","time_local":"03/Apr/2014:03:23:06 +0800"}
# >> }

您需要将代码转换为读取和写入文件,或者读取单个文件并重定向输出以捕获它。您还需要弄清楚如何确定文件中的行数,以便找到输出逗号的位置。这很简单,我知道在Stack Overflow上存在这样做的例子。

在原始代码中,您需要使用open的块表单,而不是分配给变量然后显式关闭文件;使用open的块形式是Ruby方式。另外,使用File.foreach逐行读取文件。

答案 1 :(得分:0)

@theTinMan发布了一个很好的答案,我只想补充一点JSON对象列表的更通用的解决方案是自己构建列表,但让JSON生成每个项目:

fd1.puts '{'
fd1 = File.open("foo.json", "w")
fd2 = File.open("foo.log")
first_line = true
fd2.each_line do | line |
  fd1.puts(',') unless first_line
  first_line = false
  l = fd2.lineno
  remote_addr = line.split(" ")[0]
  time_local = line.split("]")[0].split("[")[1]
  fd1.print "\"#{l}\": #{JSON.generate(:remote_addr => remote_addr, :time_local => time_local)}"
end
fd1.puts "\n}"