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

Если все числа целые, то с помощью GNU awk, который поддерживает границы слов \<...\>, вы можете сделать

gawk 'gsub(/\<0\>/, "0") <5' infile
-1
15.05.2020, 08:45
2 ответа

Вот awkрешение:

awk -F'\\|~\\^' '{ 
            if($1=="H"){ 
                head=$0
            }
            else if($1=="T"){
                foot=$1"|~^"$2
                foot4=$4
            }
            else{
                date=$3;
                sub("T.*","", date);
                data[date][NR]=$0;
                sum[date]+=$4; 
                num[date]++
            }
           }
           END{
            for(date in data){
                file=date".txt";
                gsub("-","",file); 
                print head > file; 
                for(line in data[date]){
                    print data[date][line] > file
                } 
                printf "%s|~^%.3f|~^%s|~^%.3f\n", foot, num[date], 
                                              foot4, sum[date] > file
            }
           }' file 

При запуске с вашими примерами данных получается:

$ cat 20190305.txt
H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-05T12:33:52.27|~^105603.042|~^2018-10-23T12:33:52.27|~^aus
R|~^abc|~^2019-03-05T12:33:52.27|~^2054.026|~^2018-10-24T12:33:52.27|~^usa
R|~^abc|~^2019-03-05T12:33:52.27|~^30.00|~^2018-08-05T12:33:52.27|~^ddd
R|~^abc|~^2019-03-05T12:33:52.27|~^20.00|~^2018-07-23T12:33:52.27|~^audg
T|~^20200425|~^4.000|~^xxx|~^107707.068

$ cat 20190306.txt
H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-06T12:33:52.27|~^123562388.23456|~^2018-04-12T12:33:52.27|~^hhh
R|~^abc|~^2019-03-06T12:33:52.27|~^10.00|~^2018-09-11T12:33:52.27|~^virginia
R|~^abc|~^2019-03-06T12:33:52.27|~^15.03|~^2018-10-23T12:33:52.27|~^jjj
R|~^abc|~^2019-03-06T12:33:52.27|~^10.04|~^2018-04-08T12:33:52.27|~^jj
T|~^20200425|~^4.000|~^xxx|~^123562423.305

Кроме того, поскольку вы, кажется, думаете, что awk работает медленно, я проверил это на большом файле. Чтобы создать тестовый файл, я запустил это:

perl -e '@d=<>; print $d[0]; $str=join("",@d[1..$#d-1]); print $str x 3500000; print $d[$#d]' file > bigFile

Это создает файл размером 1,9G с 28000002 строками, где 1-я строка — это заголовок исходного файла, последняя строка — нижний колонтитул исходного файла, а строки между ними — 3,5 миллиона повторений содержимого исходного файла. Затем я запустил свой awk для этого файла (обратите внимание, что у меня достаточно оперативной памяти для этого, вам потребуется не менее 618 МБ свободной оперативной памяти):

$ time awk -F'\\|~\\^' '{ if($1=="H"){head=$0} else if($1=="T"){foot=$1"|~^"$2; foot4=$4;} else{date=$3; sub("T.*","", date);data[date][NR]=$0;sum[date]+=$4;num[date]++;} }END{for(date in data){file=date;gsub("-","",file); sub("T.*",".txt",file); print head > file; for(line in data[date]){print data[date][line] > file} printf "%s|~^%s|~^%s|~^%s\n", foot, num[date], foot4, sum[date] > file } }' bigFile 

real    2m8.603s
user    2m0.610s
sys     0m6.795s

Итак, 2 минуты на обработку 1,9 ГБ данных в 28 000 002 строк. Это довольно быстро (, хотя решение Исаака было быстрее на 1 м 30 и использует меньше памяти, поэтому пользовательский интерфейс предлагает вам использовать его вместо этого ). Я могу абсолютно гарантировать, что вы никогда не получите это так быстро с помощью цикла оболочки for. И, если уж на то пошло, с циклом R for.

1
19.03.2021, 02:21

С вашим последним редактированием вы изменили весь вопрос.

Теперь для каждой строки отметка времени должна быть преобразована в имя файла.
То есть с 2019-03-06T12:33:52.27по 20190306. Это само по себе требует некоторой обработки строк, которая никогда не бывает быстрой на любом языке.

Эта небольшая часть может быть выполнена в awk с помощью:

awk 'BEGIN{FS="\\|~\\^";OFS="|~^"}
     $1=="R"{
              t=gensub(/-/, "","g",$3)
              s=gensub(/T.*/,"",1,t);
              $3=s
            }
     1
' "file" >"file.adj"

Затем остается (ваш первоначальный вопрос )о разделении файла на основе даты даты метки времени. Список минимальных необходимых изменений::

  • для каждой строки на входе скопируйте ее в определенный (, заданный $3 )файлом.
  • пока это будет сделано, также подсчитайте строки (для каждого файла)
  • и просуммируйте значения в поле 4.
  • Когда все входные строки будут обработаны, распечатать конец каждого файла.

Весь процесс может быть выполнен в awk следующим образом:

awk 'BEGIN  { FS="\\|~\\^"; OFS="|~^" }
     $1=="H"{ header=$0; hdr=$2 }
     $1=="R"{
              t=gensub(/-/, "","g",$3)
              file=gensub(/T.*/,"",1,t);
              sum[file]+=$4
              if(count[file]==0){ print header >file }
              count[file]++
              print $0 >>file
            }
     END    {
              for( i in sum ){
                  print "T",hdr,count[i],"xxx",sum[i] >> i;
                  close(i)
                  }
            }
' "file"

Используя повторение Perl для 1 миллиона раз исходного файла, обработка всего файла занимает всего 49,32 секунды. Минимальное использование памяти (В памяти должны оставаться только суммы и подсчеты в день ). Мне кажется, это довольно быстро.

2
19.03.2021, 02:21

Теги

Похожие вопросы