LINUX.ORG.RU

Ruby: обработка больших файлов, хэши


0

0

1. Обрабатываю какие-нибудь логи следующим образом:

aaa=File.open("aaa.log")
aaa.each{ |line|
....
}

Удобно, да. Но! Логи немаленькие, каждая новая строчка каждый раз берётся с харда... Можно ли как-то без RAM-диска сделать чтобы файл целиком грузился в RAM и там уже обрабатывался?

2. Если такой-то ключ в хэше есть, изменяем соответствующее значение по ключу, если нет, заводим новый ключ. Делаю так:

if ar.keys.include? x then
ar[x]+=f
else
ar[x]=f
end

Работает, но выглядит очень коряво и топорно. Как это реализовать по-человечески?

3. Очень хочется к ключу хэша несколько значений присвоить в таком-то порядке. Как?


1. File.read
Как бы можно догадаться

2. Ну засунь в функцию или модуль. По крайней мере, читается это нормально, по сравнению с каким-нибудь "ar[x] = ar[x] + f rescue f", "ar[x] = ar[x] ? ar[x] + f : f" или например извращениями с Hash#default=, верно? Одно дело, когда код действительно коряв, другое - когда хочется сделать его изящнее "из любви к искусству". Получишь пару оплеух от коллег, поймешь)

3. не распарсил.

volh ★★
()
Ответ на: комментарий от volh

> File.read

Тогда его вышеописанным способом обрабатывать не получится, надо другие какие-то извращение придумывать для работы со строками. Пока сообразил вот такое:

ar=Array.new{""} aaa=File.open("log.log") aaa.each{ |line| ar << line } aaa.close

Смущает, что процесс при этом занимает в памяти аккурат в 4 раза больше исходного файла. Пока это проблемы не создаёт, но всё равно забавно.

> другое - когда хочется сделать его изящнее "из любви к искусству". Получишь пару оплеух от коллег, поймешь)

Просто выглядит как Бейсик в чистом виде. Ну да ладно, понял :)

> не распарсил

Ну допустим,

(zonenum="Россия";zone="Москва";tarif=0.00;prefix="8495") if num2.match(/^8495/)

Миллион таких строчек. Ну не дело их так if'ами проверять, хочу это в виде какого-то массива или хэшей завернуть, чтобы по ключу "8495" выбирались остальные три параметра. Или это тоже будет из серии предыдущего вопроса? :)

tx
() автор топика
Ответ на: комментарий от tx

>Тогда его вышеописанным способом обрабатывать не получится

Каким способом? each aka each_line? Все работает, ты попутал что-то.

>(zonenum="Россия";zone="Москва";tarif=0.00;prefix="8495") if num2.match(/^8495/)


господи иисусе

>чтобы по ключу "8495" выбирались остальные три параметра


hsh = {8495 => {:a => 1, :b = 2, :c = 3}}, доступно?

volh ★★
()
Ответ на: комментарий от volh

> господи иисусе

зато очевидно

> hsh = {8495 => {:a => 1, :b = 2, :c = 3}}, доступно?

Не доступно. Как оттуда эти значения получить-то?

tx
() автор топика
Ответ на: комментарий от sdio

Мда. Хотя после того, каким способом в руби предлагается создавать двумерные массивы, я уже ничему не удивляюсь. :)

tx
() автор топика
Ответ на: комментарий от volh

> а что, способ сильно отличается от аналогичного в других ЯП?

А что, нет?

int ar[10][10] или dim ar(10,10)

и

ar=Array.new(10).map!{ Array.new(10) }

tx
() автор топика

>1.

for line in File.readlines("file") do ... end

anonymous
()

1. IO.readlines("aaa.log").each {|line ...

2. ar[x] = ar.fetch(x, "") + f

3. Сохраняй в качестве значения в хэше нужную тебе структуру.

smh ★★★
()
Ответ на: комментарий от sdio

>Для обработки логов было бы логично исп. перл.
Почему? Не флейма ради, смысл учить несколько близких языков, если эти же задачи можно сделать на одном?

anonymous
()
Ответ на: комментарий от tx

>Мда. Хотя после того, каким способом в руби предлагается создавать двумерные массивы, я уже ничему не удивляюсь. :)

Там не массивы, а ArrayList-ы, т.е. списки с ассоциативным доступом. Списки не нужно создавать, их нужно динамически формировать

anonymous
()
Ответ на: комментарий от sdio

Для обработки логов было бы логично исп. перл.

Руби практически эквивалентен в этой задаче, например:

for line in File.readlines("/proc/cpuinfo")
  puts $1 if line =~ /^model name\s+: (.+)$/ 
end
anonymous
()

скажите, в руби есть метод удаляющий строку? о_О или же нада перезаписывать файл?

ciss
()
Ответ на: комментарий от smh

>2. ar[x] = ar.fetch(x, "") + f

дубовый вариант в первом сообщении все равно понятнее, а если там нужно будет использовать не только строки, то это еще и не сработает.

volh ★★
()
Ответ на: комментарий от anonymous

> Руби практически эквивалентен в этой задаче, например:

Ага, только память жрет как не в себя и дико тормозит. А так - да, вполне даже себе аналогичен :)

ps: Я как-то решил было вылить данные из базы и чуть-чуть обработать. Статистику там посчитать и пр. мелочи. Данных прямо скажем не много, порядка 1-2M записей. Выливались они на руби-в-лоб около часа, сожрало это все порядка 3-4Gb и в конечном итоге рухнуло в кору. Ага. Аналогичный же скриптик на перле или программка на C сделало все то же самое на порядкИ быстрее и памяти скушало совсем чуть-чуть.

pps: Руби - чудный язык. Но я бы сто раз подумал, прежде чем пытаться использовать его для обработки логов. Обычные мелкие логи под несколько сотен мегабайт и руби тут станет очень и очень дурно. Скорее всего.

bibi
()
Ответ на: комментарий от volh

> дубовый вариант в первом сообщении все равно понятнее, а если там нужно будет использовать не только строки, то это еще и не сработает.

fetch работает в любом случае, просто я пример привел для строк конкретно, потому что ТС, как я понял, со строками работает. Метод fetch конкретно для его задачи и создан: вернуть дефолтное значение, если его нет в хэше, поэтому нафиг городить эти if include? else end.

smh ★★★
()
Ответ на: комментарий от bibi

>bibi
>Обычные мелкие логи под несколько сотен мегабайт и руби тут станет очень и очень дурно. Скорее всего.

Ничего себе. Для Питона не проверял, как он с этим справляется?

anonymous
()
Ответ на: комментарий от anonymous

> Ничего себе. Для Питона не проверял, как он с этим справляется?

Нет, для питона не проверял. Мне нужно было конкретные статы выбрать а не ЯП :)

bibi
()
Ответ на: комментарий от bibi

>Данных прямо скажем не много, порядка 1-2M записей. Выливались они на руби-в-лоб около часа, сожрало это все порядка 3-4Gb и в конечном итоге рухнуло в кору. Ага. Аналогичный же скриптик на перле или программка на C сделало все то же самое на порядкИ быстрее и памяти скушало совсем чуть-чуть.

Если статистика считалась путем запихивания select all всех xМ записей в массив и последующей его обработки, то ССЗБ. Руби всегда будет сливать на таких [неразборчиво] прямых решениях ввиду своей ооп-модели. Есть кстати такой антипраздник - когда рельсист-новичек впервые выполняет SomeFatModel.find(:all).each {|item| some_function(item) } на большом кол-ве записей, обычно заставляют неделю носить рубиновый колпак с надписью "self.include?(:brain)"

>Но я бы сто раз подумал, прежде чем пытаться использовать его для обработки логов. Обычные мелкие логи под несколько сотен мегабайт и руби тут станет очень и очень дурно. Скорее всего.


В 1.9 с этим все вобщем почти так же хорошо, как и в перле. Надо будет проверить на чем-нибудь большом, да.

volh ★★
()
Ответ на: комментарий от volh

> SomeFatModel.find(:all).each {|item| some_function(item) }

А как правильно?

l5k
()
Ответ на: комментарий от volh

Есть кстати такой антипраздник - когда рельсист-новичек впервые выполняет ...

Это где так?

anonymous
()
Ответ на: комментарий от volh

> Если статистика считалась путем запихивания select all всех xМ записей в массив и последующей его обработки, то ССЗБ. Руби всегда будет сливать на таких [неразборчиво] прямых решениях ввиду своей ооп-модели. Есть кстати такой антипраздник - когда рельсист-новичек впервые выполняет SomeFatModel.find(:all).each {|item| some_function(item) } на большом кол-ве записей, обычно заставляют неделю носить рубиновый колпак с надписью "self.include?(:brain)"

Я привел пример попытки использования руби именно как 'повседневного' инструмента без привязки к тем же рельсам и иже с ними. И, да, влоб в моём случае 'слить все в хеш и посчитать' было выбрано не из-за лени но специфики обсчета, когда требуются все данные причем одновременно.

Загнать в хеш несколько десятков миллионов мелких (десятки байт) структурок на том же C - это копейки. Расход памяти считай что 1:1. Сделать это на перле чуть накладнее, но вполне подъемно. Сделать же это на руби... Лучше не стоит. Почему так - это уже вопрос десятый. На выходе все равно заключение вида 'не работает'.

ps: Что, впрочем, не значит, что язык плохой. Задачи у него чуть другие, нежели обработка больших массивов данных. Это как на фортране веб-сайты лабать.

bibi
()
Ответ на: комментарий от bibi

>И, да, влоб в моём случае 'слить все в хеш и посчитать' было выбрано не из-за лени но специфики обсчета, когда требуются все данные причем одновременно.

Я почти уверен, что так не делают ни в руби, ни в перле, ни где-нибудь еще ( в вышеприведенных масштабах ). Но все-таки озвучь пожалуйста задачу.

>Загнать в хеш несколько десятков миллионов мелких (десятки байт) структурок на том же C - это копейки. Расход памяти считай что 1:1. Сделать это на перле чуть накладнее, но вполне подъемно. Сделать же это на руби... Лучше не стоит.


Это так... очевидно.

volh ★★
()
Ответ на: комментарий от volh

> Я почти уверен, что так не делают ни в руби, ни в перле, ни где-нибудь еще ( в вышеприведенных масштабах ). Но все-таки озвучь пожалуйста задачу.

Всех деталей задачи я уже не помню, это была обычная проходная мелочь с год или около того назад. Впрочем, IMHO это не имеет никакого значения. Вполне достаточно представить себе задачу, в которой требуется работа с относительно большим кол-вом разнородных данных.

> Это так... очевидно.


...лишь после первого реального испытания. До этого момента мне и в голову не могло прийти, что оверхед по памяти будет не в 2-3 раза что в принципе простительно, но чуть ли не в 10ть.

bibi
()

зачем изобретать велосипед? Быстренько загнать в базу, SQlite например, и уже потом из нее тягать что надо.

Apkawa
()
Ответ на: комментарий от Apkawa

> зачем изобретать велосипед? Быстренько загнать в базу, SQlite например, и уже потом из нее тягать что надо.

Обращение по IP по каждой строчке... Не думаю что сама база внутри себя сможет делать все необходимые выверты которые надо сделать с этими данными. База всё же для хранения и быстрого поиска, а не для обработки.

Делать же в программе разнообразыне кэширования чтобы меньше дрючить базу это уже будет "база ради базы". Для других данных может это и пригодилось бы, здесь же выборки простейшие, структуры маленькие, но их МНОГО и все надо обрабатывать.

tx
() автор топика
Ответ на: комментарий от tx

>Обращение по IP по каждой строчке

IP в данном случае не нужен, ибо SQLite - локальный _встраиваемый_ движок, в этом и вся его прелесть =)

yoghurt ★★★★★
()
Ответ на: комментарий от ciss

А в C++ есть метод удаляющий строку? или же нада перезаписывать файл?

Karapuz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.