| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > Perl: Общие вопросы > Посчитать количество элэментов по двум параметрам |
| Автор: makk 20.7.2014, 12:20 |
| Приветствую всех! Прошу помощи в весьма простой задаче, я не программист перл, раньше работал с ним, но это было 7 лет назад. Теперь работаю с другими языками. Написать задачу нужно именно на Перл, поэтому другой язык использовать не могу. Задача следующая, имеется текстовый документ ... в каждой строке записаны данные в формате 27.06.2014, Data1, Namber, Data2, IDData, Data3 где Data3 имеет всего 4 значения .... Список будет примерно таким .. 27.06.2014, Data1, Namber, Data2, IDData, Data3 27.06.2014, Data1, Namber, Data2, IDData, Data3 27.06.2014, Data1, Namber, Data2, IDData, Data3 27.06.2014, Data1, Namber, Data2, IDData, Data3 27.06.2014, Data1, Namber, Data2, IDData, Data3 27.06.2014, Data1, Namber, Data2, IDData, Data3 27.06.2014, Data1, Namber, Data2, IDData, Data3 Важно уточнить ... что ориентация на длинну строки шаблонно сделать нельзя, т.к. некоторые параметры постоянно разные. Все остальные данные меня не интересуют, мне нужно посчитать сколько раз повторяется Data3 из доступных 4 вариаций ... сложность в том, что поиск я думаю должен быть рекурсивный, по причине того что при сравнении Data3, нужно учитывать состояние Data2 .... Data2 это номера от 1 до 255 ... При этом ... если ... Data2 имеет 2 одинаковых значения из 4 ых возможных .. то они не учитываются, а при первом совпадении если имеется строке один из параметров .. происходит переход на следующую строку. Тоесть поиск по двум параметрам в одной строке ... и их подсчет. Алгоритм на словах такой ... Поиск в каждой строке .... условие примерное начало файла данных, Data2 номера как я и сказал, заменил их .. 27.06.2014, Data1, Namber, 1, IDData, Data3_1 27.06.2014, Data1, Namber, 1, IDData, Data3_2 27.06.2014, Data1, Namber, 1, IDData, Data3_1 27.06.2014, Data1, Namber, 2, IDData, Data3_4 27.06.2014, Data1, Namber, 2, IDData, Data3_4 27.06.2014, Data1, Namber, 2, IDData, Data3_4 27.06.2014, Data1, Namber, 2, IDData, Data3_1 Поиск идет конкретного значения например ... только Data3_1 в данных строчках .. таким образом .. в Data2 берется один ... потом сравнивается ... Data3 ... если параметр Data3_1 найден ... то мы ищем дальше уже не в Data2 равное 1 в данном случае, а переходим до тех пор пока Data2 не станет 2, где цикл поиска вновь начинается сначала ... и после 3 значений Data3_4 найдется Data3_1 и вновь перейдет на Data2 со значением 3 .... Как можно это реализовать ?! Помогите пожалуйста! Заранее большое спасибо! |
| Автор: Bulat 21.7.2014, 10:00 |
| Немного тяжело понять что в конечном счете требуется, но явно нужно копать в сторону регулярных выражений! |
| Автор: arto 21.7.2014, 14:36 |
| Если бы вы писали на правильном русском языке, то можно было бы понять, что вам надо. А так: # perl -aF"/,\\s+/" -lne '$h{ join",",@F[1..5] }++; END{ while (($a,$b) = each%h) { print $a, ": ", $b } }' Data1,Namber,2,IDData,Data3_1: 1 Data1,Namber,1,IDData,Data3_2: 1 Data1,Namber,1,IDData,Data3_1: 2 Data1,Namber,2,IDData,Data3_4: 3 |
| Автор: DProf 21.7.2014, 16:41 | ||||||||
Нет смысла использовать регулярки. Если правильно понял, то автору надо это:
Целиком:
Сработает, если текст разделен жестко по шаблону ", " (запятая + пробел) как у Автора в примере. То что написал АРТО коротко и работает, но слишком сложно для человека, который не пишет на perl. Добавлено через 2 минуты и 40 секунд Вывод:
или если через Dumper
|
| Автор: Bulat 21.7.2014, 16:55 |
Смысл как раз в том, чтобы не плодить кучу кода, а сделать все компактно и понятно, что и сделал arto |
| Автор: makk 21.7.2014, 17:06 |
| Доброго дня! Спасибо большое за быстрые ответы! Я попытался объяснить проблематику, но наврное слишком перегрузил информацией. Даже с этими примерами, я уже могу что-то доделать, я попробую сам довести до ума ... Как было сказано, при поиске к примеру в этом массиве по двум парамертрам результат будет следующий ... Где происходит поиск - # in file filename this: # 27.06.2014, Data1, Namber, 1, IDData, Data3_1 # 27.06.2014, Data1, Namber, 1, IDData, Data3_2 # 27.06.2014, Data1, Namber, 1, IDData, Data3_1 # 27.06.2014, Data1, Namber, 2, IDData, Data3_4 # 27.06.2014, Data1, Namber, 2, IDData, Data3_4 # 27.06.2014, Data1, Namber, 2, IDData, Data3_4 # 27.06.2014, Data1, Namber, 2, IDData, Data3_1 Первый запрос на поиск ... ищем ... Data3_1 Выход будет Data3_1 = 2 .... по той причине, что в Data2 где стоят три единицы, это можно сказать еще один ИД номер, где мы считаем всего один раз, при первом совпадении, то есть строка номер 3 уже не будет учитываться, в итоге в следующий строки покажут тоже наличине только одной Data3_1 ... где сумма их будет два. Пример с поиском Data3_4 .... Выход будет Data3_4 = 1 так как в четырех ИД номера под 2 цифрой их три ... но мы считаем только один раз про ИД .. первые ид номера это 1 1 1 и потом 2 2 2 2 .. в каждом из них только один раз считаем искомую константу. Вывод общий должен быть такой .. Data3_4 count 1 num Data3_2 count 1 num Data3_1 count 2 num По этой причине нужно учитывать два параметра ... а не только искомую величину .. отсеивая таким образом повторные искомые константы в каждом одинаковом ИД номере. Спасибо вам большое! |
| Автор: Bulat 21.7.2014, 17:12 | ||
Чет все равно не особо понял чего, и где искать, и зачем искать! |
| Автор: makk 21.7.2014, 17:18 |
| Посмотрите на результаты поиска которые я привел .. может быть по ним будет ясно, если коротко, это поиск по двум параметрам, где один из параметров имеет только 4 значения и должен быть подсчитан один раз учитывая второй параметр. Считать нужно т.к. таких строчек около 40 000 ... и много файлов с таким же объемом. |
| Автор: Bulat 21.7.2014, 17:59 |
| makk, у тебя в строке 6 столбцов?? Какие конкретно столбцы нужно учитывать по номерам (если за первый столбец взять тот что слева)?? |
| Автор: DProf 22.7.2014, 11:44 | ||||
| Какие то странные условия Вы ставите, уважаемый makk. Но может вот так:
Вывод:
Если все верно, пару слов скажите, где Вы это используете. |
| Автор: Bulat 22.7.2014, 13:03 | ||
И сколько раз придется переписывать и перепроверять многострочный скрипт при небольшом изменении условий, тем более человеку не знающему перл, в то время когда несколько строк через регэкспы - нужно патчить всего эти пару строк. |
| Автор: DProf 22.7.2014, 13:14 | ||||
| Не согласен с Вами, Bulat. Изменять приведенный скрипт очень легко, ведь сразу понятно где что. Большим он тоже выглядит только потому, что много комментариев. Изменения в соответствии с уточненными условиями я внес очень быстро. А если я неправ, перепишите ка мне однострочник от АРТО с соответстии с условиями автора, чтобы было
Хотя если на разделе для файла с кодом всего килобайт памяти, то да, это может быть проблемой Добавлено через 1 минуту и 22 секунды
а тут не поспоришь, Вы правы |
| Автор: Bulat 22.7.2014, 14:53 | ||
DProf, может уже хватит холивара, прежде чем утверждать что-либо, разумно сначала хотя бы прежде разобраться что требуется, это во-первых! А во-вторых, регулярные выражения присутствуют во многих ЯП, просто в перле их использование упрощено, а вот разбираться с перловым синтаксисом для непосвященного человека, гораздо тяжелее... И Баста!
Вот сначала нужно конкретно понять что требуется, а уже потом доказывать свою правоту! |
| Автор: arto 22.7.2014, 15:11 |
| Хотя я не понял условия задачи, но эквивалент представленного скрипта: # perl -aF"/,\\s+/" -lne '$h{ $F[5] }->{$F[3]}++; END{ while (($a,$b) = each%h) { print $a, ": ", scalar keys $b } }' Data3_1: 2 Data3_2: 1 Data3_4: 1 # |
| Автор: makk 24.7.2014, 23:24 | ||||
| Доброго вечера! Спасибо за такую поддержку, я был эти дни занят, но сейчас вновь вернулся к задаче. Мне конечно легче читать большой и читабильно понятный код, чем регехп типичные перл выражения. Я тоже люблю регулярные, но каждому инструменту свое применение. DProf уловил смысл задачи и даже написал отличный код, применить правда мне его не получилось, по причине моих неточностей, в начале я говорил о рекурсивном методе не просто так. Код написан правильно и правильно отрабатывает, было бы хорошо, если бы никто не гадал, а я бы выложил файл с данными как пример! Я постараюсь это сделать, так как сейчас просто на другом компьютере. Я попробовал изменить код написанный DProf, в целом логика понятна. Первая проблема, т.к. файлов много, я использую сформированный сгенерированный файл бат для запуска и сохранения информации. То есть из бат файла подается адрес и имя файла на поиск ... в перл файл простым параметром .. и в перл программе его открывать не нужно. Ранее я пробовал считать только один параметр через Чомп тоже, там в параметр чомп я задавал то что я ищу, так получал количество записей. В данном случае схема не меняется .. файл подается через Бат файл, где в итоге код ниже не работает ...
Извиняюсь за мой изврат, но так мне удобнее и я надеюсь смогу быстрее решить свою проблему. Вопрос .. как применить тот же код, только без открытия файла?! Теоретически Хэндл файла должен быть все равно, так как файл передается как параметр в перл скрипт, но я на тесте не смог использоваь этот хэндл. Проблема вторая ... можно ли записать код таким образом?! Как будет правильно?! Цель такая же, результаты такие же должны быть, как выше описано. Вот запись которую я хочу использовать
где строка elsif ($data3_value eq 'Data3_2') { $counts{'Data3_2'} ++; }, мне нужна как двойная запись .. пример elsif ($data3_value eq 'Data3_2') AND ($data2_value eq 'hash') { $counts{'Data3_2'} ++; } то есть двойное условие, почему так .. так как цифры 111 и 2222 этот ид это не числа в прямом смысле ... это хэши от чисел с добавлением времени ... и других параметров, это не важно как формируется хэш, так как лист хешей известен и они повторяются. Поэтому параметр где единички и двойки, нужно указывать не как число ... а как такой же текст ... В итоге подсчет по двум параметрам идет только по двум текстовым параметрам. Как я это вижу ... if ($data3_value eq 'Data3_1') { $counts{'Data3_1'} ++; } elsif ($data3_value eq 'Data3_2') AND ($data2_value eq 'hash') { $counts{'Data3_2'} ++; } elsif ($data3_value eq 'Data3_2') AND ($data2_value eq 'hash') { $counts{'Data3_3'} ++; } elsif ($data3_value eq 'Data3_2') AND ($data2_value eq 'hash') { $counts{'Data3_4'} ++; } else { die "error! Data 3 has unknow value!\n"; }; Конструкцию фор я видимо не совсем еще осознал ... что куда считает ... Не ломайте голову, я завтра возьму реальный пример ... и выложу сюда, чтобы было все ясно. Спасибо вам! |
| Автор: makk 26.7.2014, 19:47 | ||||||||||||||||
| Приветствую всех! Вот оригинальный файл, приложил к сообщению, вот отрывок из файла
Задача остается такой же, важны только последние три параметра ... предпоследний параметр всегда повторяется, он идет циклично, поэтому я хотел сделать по двум параметрам рекурсивно. Запись немного иная, но принцип тот же самый. В данном случае считать нужно IDD_1 к примеру, т.е. все последние параметры учитывая ... рост ид номера в цифрах 00001, 00002, подсчитывая только одно совпадение. Я же хотел чтобы было проще, чтобы детектировать от Data3_0 до Data3_3 ... Пример .. ищем IDD_5, результат будет такой .... IDD_5 = 5 ... считаем IDD_3 ... результат IDD_3 = 4 ... Почему можно сделать рекурсивно?! .. По той причине что в порякде от Data3_0 до Data3_3 они идут всегда от нуля .. и в целом не считаются .. То есть мы ищем к примеру IDD_3 ... 4 раза с параметрами от Data3_0 до Data3_3, если нет совпадения на новый цикл и опять вперед считать .... с таким подходом мы избавимся от подсчета в цифрах 00001, 00002, и далее .. используя внутреннию логику этого лога, который не будет изменен в дальнейшем, стандарт будет одинаковым. Вопрос с хэндлом открыт ... я не могу посчитать .. так как передаю сам файл через бат в перл скрипт как параметр ... Так выглядит мой простой подсчет в прошлом
В Total.txt идут результаты исполнения перлскрипта ... Во втором Бате такой код ..
где он принимает файл Data.txt и считает ... код самого перл скрипта выше .. Как известно он считает все подряд!!! А мне надо один раз про ИД ... Файл оригинальный, так что я исключил все недорозумения ... Очень требуется ваша помощь. Заранее спасибо!!! |
| Автор: makk 29.7.2014, 13:38 |
| Уважаемые специалисты, помогите пожалуйста, из опытов ничего не вышло, хотябы применить ранее написанный DProf скрипт для последнего файла данных. |
| Автор: arto 29.7.2014, 14:12 |
| Из вашего изложения задания ничего непонятно. Предлагаю пригласить взрослого, который сможет внятно рассказать, что вам надо. |
| Автор: makk 29.7.2014, 21:41 | ||
Приветствую! Что конкретно не понятно?! Нужно посчитать IDD_5 учитывая 00001 повышающееся число или же эквивалентный вариант учитывая Data3_1, которые по алгоритму проверяются всегда от нуля т.е. от Data3_0 до Data3_3. Посчитать с повышением счетчика на один при учете что во втором параметре их может быть больше одного. Запуск идет с бат файла, в который подается сам файл как параметр. Больше ничего ... П.С. Можно обойтись без оскорблений, иначе действительно нужно звать взрослых. |
| Автор: DProf 1.8.2014, 19:48 | ||||||
Для приведенного файла
Вызов
Файл берется из опций командной строки. Вы разберитесь у себя, потому что реально ж ничего не понятно из длинных объяснений. И при чем тут рекурсия, когда я Вам рабочий вариант выдал без всяких рекурсий. Ну если конечно именно оно и надо. А если хотите открывать много файлов, откройте в цикле и объедините из них данные. Вот пример, как сложить данные из всех файлов, названных makk_1.txt, makk_2.txt, makk_3.txt из одной папки, передаваемой в опциях командной строки.
Предупреждаю, что больше я Ваши путанные объяснения читать не буду. |
| Автор: makk 2.8.2014, 10:44 | ||||
| DProf, Спасибо большое! Задачу вы поняли правильно. На базе вашего прошлого сообщения, которое вы написали еще в самом начале этого топика, я собрал комплект, так как оно должно работать. Выложил тут на http://rghost.ru/57221801 В моем архиве видно как я вызываю из бат файла ... Вызывается Total.bat, который в свою очередь вызывает set_env_and_run_1.bat , а тот вызывает Search.pl %~1. В Total.bat будет весь сформированный список файлов, т.к. их порядок важен. В самом Перл скрипте я сделал обработку подачи из БАТ файла как и хотел методом
Имя файла передается через цепочку вызова Бат файла. Это для информации, как я хотел сделать и сделал. Потом я столкнулся с проблемой, что подсчет невозможен из-за стоящих нулей впереди в одном из параметров. Я решил сделать ТРИМ нулей, но без успеха. Команда была - # my $data2_value =~ s/\0+$//; Вторая особенность, считать IDD_нужно раздельно, т.е. выводить и искать только один из них, а не все сразу. Поэтому с IF записями не разобрался. На данный момент благодаря вам есть с чем работать дальше - единственное, что я хотел бы вас спросить по последнему скрипту в посте выше - Как можно выводить выборочно только один 'IDD_1', а не все сразу?! С возможностью потом также выводить другие ИДД одинарно. Огромное спасибо за помощь! |
| Автор: DProf 2.8.2014, 19:36 | ||||
Все результаты в хеше %counts, где ключ - имя поля (например, 'IDD_1') а соответствующее ему значение - число вхождений. То есть так:
|