Tôi đang viết tập lệnh nhập để xử lý tệp có khả năng hàng trăm nghìn dòng (tệp nhật ký). Sử dụng một cách tiếp cận rất đơn giản (dưới đây) đã mất đủ thời gian và bộ nhớ mà tôi cảm thấy như nó sẽ đưa ra MBP của tôi bất cứ lúc nào, vì vậy tôi đã giết chết quá trình.Cách phân tích cú pháp hiệu quả các tệp văn bản lớn trong Ruby
#...
File.open(file, 'r') do |f|
f.each_line do |line|
# do stuff here to line
end
end
tập tin đặc biệt này có 642.868 dòng:
$ wc -l nginx.log /code/src/myimport
642868 ../nginx.log
Có ai biết của một (bộ nhớ/CPU) cách hiệu quả hơn để xử lý mỗi dòng trong tập tin này?
CẬP NHẬT
Mã bên trong f.each_line
từ trên chỉ đơn giản là phù hợp với một regex chống lại dòng. Nếu không khớp, tôi thêm dòng vào một mảng @skipped
. Nếu nó trôi qua, tôi định dạng các kết quả phù hợp thành một băm (được khóa bởi "trường" của trận đấu) và nối nó vào một mảng @results
.
# regex built in `def initialize` (not on each line iteration)
@regex = /(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - (.{0})- \[([^\]]+?)\] "(GET|POST|PUT|DELETE) ([^\s]+?) (HTTP\/1\.1)" (\d+) (\d+) "-" "(.*)"/
#... loop lines
match = line.match(@regex)
if match.nil?
@skipped << line
else
@results << convert_to_hash(match)
end
Tôi hoàn toàn cởi mở với điều này là một quá trình không hiệu quả. Tôi có thể làm cho mã bên trong của convert_to_hash
sử dụng một lambda precomputed thay vì tìm ra các tính toán mỗi lần. Tôi đoán tôi chỉ giả định nó là bản lặp dòng chính nó là vấn đề, không phải là mã mỗi dòng.
Cách hiệu quả nhất về bộ nhớ là cách bạn đang thực hiện với 'each_line'. Bạn có thể đọc tệp trong các khối nhanh hơn, sau đó sử dụng 'Chuỗi # dòng' để lấy các dòng riêng lẻ cùng với nối lại bất kỳ dòng được tải một phần nào vượt qua các ranh giới khối. Nó trở thành một rửa phải tách ra các dòng và tái tham gia những người bị hỏng. –