[英]Parsing structured file in Ruby
我想解析一個大的日志文件(大約500mb)。 如果這不是正確的工具,請告訴我。
我有一個日志文件,其內容結構如下。 每個部分都可以有額外的鍵值對:
requestID: saldksadk
time: 92389389
action: foobarr
----------------------
requestID: 2393029
time: 92389389
action: helloworld
source: email
----------------------
requestID: skjflkjasf3
time: 92389389
userAgent: mobile browser
----------------------
requestID: gdfgfdsdf
time: 92389389
action: randoms
我想知道是否有一種簡單的方法來處理日志中的每個部分的數據。 一個部分可以跨越多行,所以我不能只分割字符串。 例如,有一種簡單的方法可以做這樣的事情:
for(section in log){
// handle section contents
}
使用icktoofay的想法,並使用自定義記錄分隔符 ,我得到了這個:
require 'yaml'
File.open("path/to/file") do |f|
f.each_line("\n----------------------\n") do |line|
puts YAML::load(line.sub(/\-{3,}/, "---")).inspect
end
end
輸出:
{"requestID"=>"saldksadk", "time"=>92389389, "action"=>"foobarr"}
{"requestID"=>2393029, "time"=>92389389, "action"=>"helloworld", "source"=>"email"}
{"requestID"=>"skjflkjasf3", "time"=>92389389, "userAgent"=>"mobile browser"}
{"requestID"=>"gdfgfdsdf", "time"=>92389389, "action"=>"randoms"}
這看起來像 YAML ,雖然它不完全是YAML。 (YAML用三個破折號分隔文檔,不再是。)您可能會嘗試以某種方式破壞文檔,使得僅由連字符組成的行折疊成三個連字符,因此它是有效的YAML。 之后,您可以將其提供給YAML解析器。
我將示例文本保存到名為“test.txt”的文件中。 打開它:
File.foreach('test.txt').slice_before(/^---/).to_a
收益:
[
["requestID: saldksadk\n", "time: 92389389\n", "action: foobarr\n"],
["----------------------\n", "requestID: 2393029\n", "time: 92389389\n", "action: helloworld\n", "source: email\n"],
["----------------------\n", "requestID: skjflkjasf3\n", "time: 92389389\n", "userAgent: mobile browser\n"],
["----------------------\n", "requestID: gdfgfdsdf\n", "time: 92389389\n", "action: randoms\n"]
]
通過過濾器運行每個子陣列,我們可以去除前導“---”:
blocks = File.foreach('test.txt').slice_before(/^---/).map { |ary|
ary.shift if ary.first[/^---/]
ary.map(&:chomp)
}
運行后blocks
是:
[
["requestID: saldksadk", "time: 92389389", "action: foobarr"],
["requestID: 2393029", "time: 92389389", "action: helloworld", "source: email"],
["requestID: skjflkjasf3", "time: 92389389", "userAgent: mobile browser"],
["requestID: gdfgfdsdf", "time: 92389389", "action: randoms"]
]
再調整一下:
blocks = File.foreach('test.txt').slice_before(/^---/).map { |ary|
ary.shift if ary.first[/^---/]
Hash[ary.map{ |s| s.chomp.split(':') }]
}
和blocks
將是:
[
{"requestID"=>" saldksadk", "time"=>" 92389389", "action"=>" foobarr"},
{"requestID"=>" 2393029", "time"=>" 92389389", "action"=>" helloworld", "source"=>" email"},
{"requestID"=>" skjflkjasf3", "time"=>" 92389389", "userAgent"=>" mobile browser"},
{"requestID"=>" gdfgfdsdf", "time"=>" 92389389", "action"=>" randoms"}
]
您可以逐行讀取文件。 對於每一行,我們將檢查它是記錄分隔符還是鍵:值對。 如果是前者,我們會將當前記錄添加到記錄列表中。 如果是后者,我們將k:v對添加到當前記錄中。
records = []
record = {}
open("data.txt", "r").each do |line|
if line.start_with? "-"
records << record unless record.empty?
record = {}
else
k, v = line.split(":", 2).map(&:strip)
record[k] = v
end
end
records << record unless record.empty?
這會產生類似於:
[{"requestID"=>"saldksadk", "time"=>"92389389", "action"=>"foobarr"},
{"requestID"=>"2393029", "time"=>"92389389", "action"=>"helloworld", "source"=>"email"},
{"requestID"=>"skjflkjasf3", "time"=>"92389389", "userAgent"=>"mobile browser"},
{"requestID"=>"gdfgfdsdf", "time"=>"92389389", "action"=>"randoms"}]
非常基本的方法,使它簡單有效:
blocks = []
current_block = {}
sep_range = 0..3
sep_value = "----"
split_pattern = /:\s*/
File.open("filename.txt", 'r') do |f|
f.each_line do |line|
if line[sep_range] == sep_value
blocks << current_block unless current_block.empty?
current_block = {}
else
key, value = line.split(split_pattern, 2)
current_block[key] = value
end
end
end
blocks << current_block unless current_block.empty?
需要注意的一點是,我們避免在循環內創建不必要的重復對象(范圍,測試字符串和拆分正則表達式模式),而是在循環開始之前定義它們,這樣可以節省一點時間和內存。 在500mb的文件上,這可能很重要。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.