LINUX.ORG.RU

маленькая програмка чтобы вытащить числа из лога


0

0

пару недель назад столкнулся с такой проблемой. был лог, и из этого лога нужно было вытащить числа, сложить и посчитать среднее арифметическое. я не нашёл никакой тулзы или редактора который бы мог сделать это автоматом и не нужно было клацать на калькуляторе 150 чисел вручную (хотя не исключено что вим это умеет). вот кусок лога: [demod/stv362/s362lock.c:189][0] s362_Lock() Status[2] Time[500ms] [demod/stv362/s362lock.c:189][0] s362_Lock() Status[2] Time[400ms] [demod/stv362/s362lock.c:189][0] s362_Lock() Status[2] Time[1800ms] [demod/stv362/s362lock.c:189][0] s362_Lock() Status[2] Time[500ms] мне нужно было вытащить эти самые миллисекунды. я решил не заморачиваться поисками и написал такую простенькую программку, которая это делает. в данный момент нет поддержки связанных списков для файлов неопределённого размера, не поддерживается обработка того, что время попадается 2 раза в строке, и нет защиты от переполнения unsigned int переменной. В общем-то хотелось бы послушать какие ещё тут могут быть бока, а также про неоптимальность алгоритма и т.д. и т.п. ;)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


char *version="0.53";
#define CHARS_PER_STRING 300
//#define DEBUG_ENABLE


FILE * file_ptr;
int times[300];  // strings in file

int FileScan(FILE* file_ptr, const char * sequence)
{
	unsigned int i;
	unsigned int sequence_size=0;
	unsigned int total=0;
	unsigned char have_sequence=0;
	char tmp_string[CHARS_PER_STRING];   //array to contain one string
	char * tmp_str_ptr;
  char digit[10];
  int digit_cnt=0;
	unsigned int strings_seq_cnt=0;     //strings with sequence found
	unsigned int strings_total_cnt=0;   //total number of strings in file

	sequence_size=strlen(sequence);
  
#ifdef DEBUG_ENABLE
	printf ("sequence: %s \n", sequence);
	printf("size of seq %d\n", sequence_size);
#endif

	//working till the EOF, one iteration == one string have read
	while ( fgets(tmp_string,CHARS_PER_STRING,file_ptr) != NULL )
	{
		tmp_str_ptr=tmp_string;
		have_sequence=0;
  		while (*tmp_str_ptr)
  		{
    			if (*tmp_str_ptr == *sequence)
    			{
      				if ( !strncmp(tmp_str_ptr,sequence,sequence_size) ) //if we found Time[
      				{
#ifdef DEBUG_ENABLE
					printf("seq found\n");
#endif
		        		tmp_str_ptr += sequence_size ;   //set pointer to time digits
        				while (*tmp_str_ptr >= '0' && *tmp_str_ptr <= '9')
        				{
	  					if (digit_cnt >= 10)
	  					{
	  						printf("[error] value found is too big. \n");
							return 1;
	  					}
          					digit[digit_cnt]=*tmp_str_ptr;
          					tmp_str_ptr++;
          					digit_cnt++;
	  					have_sequence=1;
        				}
#ifdef DEBUG_ENABLE
					printf ("digit %d \n",digit_cnt);
#endif
      				}
    			}
    			tmp_str_ptr++;
  		}

		if (have_sequence)
		{
			times[strings_seq_cnt]=atoi(digit);
			strings_seq_cnt++;
		}
  		for (i=0; i<10; i++)
    			digit[i]='a';
  		strings_total_cnt++;  //increased for every cycle iteration, contains number of strings in file
  		digit_cnt=0;

	}

	for (i=0; i<strings_seq_cnt; i++)
 	{
   		total+=times[i];
   		printf ("string: %d time : %d \n",i+1,times[i]);
 	}
 	printf ("strings total:         %d \n",strings_total_cnt);
	printf ("strings with sequence: %d \n",strings_seq_cnt);
	printf ("time sum:              %d \n",total);
 	if (strings_seq_cnt)
		total/=strings_seq_cnt;
	printf ("time~: %d\n",total);

	return 0;
}


int main (int argc , char* argv[])
{
	unsigned int i;

#ifdef DEBUG_ENABLE
	printf("timefromlog started. version: %s\n",version);
	printf("argc=%d\n",argc);
	for (i=0; i<argc; i++)
	{
		printf("argv[%d]: %s\n",i,argv[i]);
	}

#endif
	//parsing input without getopt() ! 
	if (argc == 2)
	{
		if ( !(strcmp(argv[1],"--help") ) )  
			printf ("this is a help for timefromlog program.\nfirst param: filename to search in.\nsecond param: sequence digits start after.\n --help    - this help\n --version - program version\n");
		else if ( !(strcmp(argv[1],"--version") ) )  
			printf ("version: %s  \n", version);
		else
			printf("incorrect params. run program with --help\n");
		return 1;
	}       
	else if (argc != 3)
	{
		printf("incorrect params. run program with --help\n");
		return 1;
	}

	file_ptr=fopen(argv[1],"r");
	if (!file_ptr)
	{
		printf("[error] : can't open file\n");
		return 1;
	}
	else 
		printf("[working] : file opened\n");

	FileScan(file_ptr, argv[2]);
	
	if ( !fclose(file_ptr) )
		printf("[working] : file closed successfully\n");
	else 
		printf("[error] : file can not be closed\n");
  

  return 0;
}

Re: маленькая програмка чтобы вытащить числа из лога

есть такая тулза, называется perl

Reset ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

А в чем профит С в данной задаче?
Например на перле, без всех твоих ограничений, примерно так:

#!/usr/bin/perl

use strict;

my $file = shift;
my $sum = 0;
my $count = 0;

open(FILE, "<", $file)
or die("Can't open file $file: " . $!);

while (my $line = <FILE>) {
while ($line =~ /Time\[(\d+)ms\]/g) {
$sum += $1;
$count++;
}
}

close(FILE);

print "$sum/$count=" . $sum/$count . "\n";

tzukko ()

Re: маленькая програмка чтобы вытащить числа из лога

сурово

jtootf ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Надо бы тоже завести себе фейковую асеч^Wаккаунт и троллить местных лиспофагов сиплюсплюсом, а остальных - турбопаскалем и вба.

mannaz ()

Re: маленькая програмка чтобы вытащить числа из лога

Очень рекомендую почитать что-нибудь про стандартные утилиты (coreutils, moreutils, sed, awk и т.д.), а так же про программирование шелл-скриптов.

Вот заменитель для всей твоей программы:

num=$(( $( cat logfile | grep -oe "\[[0-9]*ms\]" | wc -l ) )); cat logfile | grep -oe "\[[0-9]*ms\]" | ( printf "("; sed "s/^\[\([0-9]*\)ms\]$/\1+/" | tr -d "\n"; echo "0)/${num}" ) | bc -l

mironov_ivan ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

>Надо бы тоже завести себе фейковую асеч^Wаккаунт и троллить местных лиспофагов сиплюсплюсом, а остальных - турбопаскалем и вба.

Да, не стоит - быстро приестся.

tzukko ()

Re: маленькая програмка чтобы вытащить числа из лога

> num=$(( $( cat logfile | grep -oe "\[[0-9]*ms\]" | wc -l ) )); cat logfile | grep -oe "\[[0-9]*ms\]" | ( printf "("; sed "s/^\[\([0-9]*\)ms\]$/\1+/" | tr -d "\n"; echo "0)/${num}" ) | bc -l

на perl всё ж проще и без лишнего cat,grep:

cat logfile | perl -e '$lc=$lm=$sum=0; while(<>){ if( /Time\[(\d+)ms\]/ ) { $sum+=$1; $lm++; } $lc++; } print "total lines: $lc\nmatched: $lm\nsumm: $sum\n"; print "avg: ".( $sum / $lm )."\n" if( $lm );'

FELiX ()

Re: маленькая програмка чтобы вытащить числа из лога

> Вот заменитель для всей твоей программы:

> num=$(( $( cat logfile | grep -oe "\[[0-9]*ms\]" | wc -l ) )); cat logfile | grep -oe "\[[0-9]*ms\]" | ( printf "("; sed "s/^\[\([0-9]*\)ms\]$/\1+/" | tr -d "\n"; echo "0)/${num}" ) | bc -l


А на lex'е-то покороче будет... Тем более, автор так Си любит ;)

mv ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Вот вариант на lex'е:

%{
	int total = 0;
%}
%%
Time\[([0-9]+)ms\]	total += atoi(yytext + 5);
.
%%

int yywrap ()
{
	return 1;
}

int main()
{
	yylex();
	printf("total = %d\n", total);
	return 0;
}

Автор, не пиши парсеры на Си!

mv ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

толсто

dilmah ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Предлагаю придумать максимально короткий вариант на любом Я.П.! Имя файла с логами - logfile. Программа должна просто вывести число (среднее значение). Производительность значения не имеет.

Мой вариант на шелле:

C="grep -o '\[[0-9]*ms\]' logfile";n=$(eval $C|wc -l);eval $C|(printf "(";grep -o "[0-9]*"|tr "\n" "+";echo "0)/$n")|bc -l

123 символа =).

mironov_ivan ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Проще завести тсудента-практиканта, а лучше практикантку.

wfrr ★★☆ ()
Ответ на: никак не отпускает! от mironov_ivan

Re: никак не отпускает!

>C="grep -o \[[0-9]*ms\] logfile";eval $C|(echo "($(tr -d [ms]|tr "\n" +)0)/$(eval $C|wc -l)")|bc -l > 99

cat logfile|perl -e 'while(<>){if(/\[(\d+)ms\]/){$a+=$1;$b++;}}print $a/$b;'

76

FELiX ()
Ответ на: никак не отпускает! от mironov_ivan

Re: никак не отпускает!

> 99

93

%option noyywrap
%{
i;
%}
%%
[0-9]+m i+=atoi(yytext);
.
%%
main(){yylex();printf("%d\n",i);}

mv ★★★★★ ()
Ответ на: Re: никак не отпускает! от mv

Re: никак не отпускает!

> В одной строке много значений может быть.

cat logfile|perl -e 'while(<>){while(s/\[(\d+)ms\]//){$a+=$1;$b++;}}print $a/$b;'

81

FELiX ()
Ответ на: Re: никак не отпускает! от FELiX

Re: никак не отпускает!

Можно еще соптимизировать слегка:

perl -e 'while(<>){while(/\[(\d+)ms\]/g){$a+=$1;$b++;}}print $a/$b;' logfile

75

tzukko ()

Re: маленькая програмка чтобы вытащить числа из лога

народ вы молодцы! ) 1. мне очень понравилась притча. она подходит к ситуации идеально ;) 2. честно говоря ожидал услышать кучу наездов на код, и в общем-то интересно было про алгоритм, но все пошли другим путём. 3. ссылка на аглицкий форум - там тоже мой пост.

какой я для себя сделал вывод - нужно восполнить пробел и хорошо разобраться в шелл скриптах, чтобы не страдать такой фигнёй )

repu1sion ()

Re: маленькая програмка чтобы вытащить числа из лога

#include <QFile>
#include <QRegExp>
#include <stdio.h>

int main() {
	QFile f;
	QRegExp r( "(\\d+)ms" );
	f.open( stdin, QFile::ReadOnly );
	int a=0, b=0, c;
	while( !f.atEnd() ) {
		c = 0;
		QString l = f.readLine();
		while( ( c = r.indexIn( l , c ) ) != -1 ) {
			a += r.cap(1).toInt();
			b++;
			c += r.matchedLength();
		}
	}
	printf( "%d\n", a/b );
	return 0;
}

summatus ()
Ответ на: Re: никак не отпускает! от mironov_ivan

Re: маленькая програмка чтобы вытащить числа из лога

>> perl -e
>> 75
>  Перл всё-таки рулит =).

Таки мы с shell+coreutils рулим :-)
61
grep -Eo '[0-9]+ms' logfile|awk -Fm '{a+=$1};END{print a/NR}'

sdio ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Желаете наезды на код? Ладно покормлю вас, раз так старались.

>нет поддержки связанных списков для файлов неопределённого размера

а зачем вы вобще записываете числа в массив? Нашли число, сразу печатайте его.

>printf("[error] value found is too big. \n");

Сообщения об ошибках принято выводить на stderr. Незнаю зачем, но так принято.

>нужно восполнить пробел и хорошо разобраться в шелл скриптах

Ещё можете восполнить пробел в библиотечных функция GNU libc, например, strstr() --- поиск подстроки в строке и strtol() --- аналог atoi, но установит errno при большом числе и даёт указатель на первую "не цифру" в строке.

mky ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Дайте уж и я внесу свою лепту =) GNU Smalltalk:

| f t c|
t := 0.0.
c := 0.
f := (File name: 'logfile.txt') readStream.
f linesDo: [:line |
    time := (line tokenize: ' ') last.
    t := t + (time copyFrom: ((time indexOf: $[) + 1)
                         to: ((time indexOf: $m
                                 startingAt: (time indexOf: $[)) - 1))
             asInteger.
    c := c + 1.
].
f close.
(t / c) print.

yoghurt ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

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

repu1sion ()

Re: маленькая програмка чтобы вытащить числа из лога

> эта програмка, если никто не заметил, позволяет задавать последовательность, после которой встречаются нужные цифры, почему то во всех примерах такого функционала не наблюдается ;)

Это последовательность добавляется в шаблон grep'а.

mironov_ivan ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

Ну, я отталкивался и тестил всё на том примере что с того другого форума. GNU Smalltalk тоже поддерживает регекспы кстати, да, и эту последовательность можно так же легко задать как и в предыдущих однострочниках

yoghurt ★★★★★ ()

Re: маленькая програмка чтобы вытащить числа из лога

>числа заносятся в массив чтобы посчитать сумму

И сумму считать можно сразу, массив или динамический список не нужен.

И ещё, ИМХО, сообщение вашей программы "this is a help for timefromlog program.\nfirst param: ..." сильно отличаются от общего стиля утилит командной строки. Позапускайте другие утилиты и посмотрите как они "ругаются".

ИМХО, лучше было бы что-то вида "Usage: timefromlog FILE STRPEFIX\nSearch time strings with STRPEFIX in FILE and print there sum and mean." Понятно, что это вы писали для себя, но лучше сразу стараться соблюдать общий стиль.

mky ★★★★★ ()
Ответ на: Re: никак не отпускает! от tzukko

Re: никак не отпускает!

> 75

perl -E 'map{$b++,$a+=$1 while/(\d+)ms/g}<>;say$a/$b' logfile

61. перл 5.10.0+. короче уже не могу =(

arsi ★★★★★ ()
Ответ на: Re: никак не отпускает! от arsi

Re: никак не отпускает!

> 61

perl -E 'map{$b+=s/(\d+)ms/$a+=$1/ge}<>;say$a/$b' logfile

57. теперь точно не смогу =)

arsi ★★★★★ ()
Ответ на: Re: никак не отпускает! от arsi

Re: никак не отпускает!

Какой забавный выхлоп из /dev/random, все байты - printable...

mv ★★★★★ ()
Ответ на: Re: никак не отпускает! от mv

Re: никак не отпускает!

гениальный у меня /dev/random, правда? ;) скоро он напишет скайнет и захватит мир :Р

arsi ★★★★★ ()
Ответ на: Re: никак не отпускает! от arsi

Re: никак не отпускает!

А мой императивный быдлокод на Смолтоке без регекспов всё равно читабельнее и понятнее чем эти ваши однострочники)))

yoghurt ★★★★★ ()
Ответ на: Re: никак не отпускает! от arsi

Re: никак не отпускает!

Твой вариант

perl -E '$b+=s/\d+ms/$a+=$&/ge for<>;say$a/$b' logfile

у меня не пошел -- опции Е нет и say ругается, так что вот:

perl -e'map{$b+=s/\d+ms/$a+=$&/ge}<>;print$a/$b' logfile

www_linux_org_ru ★★★★★ ()
Ответ на: Re: никак не отпускает! от mv

Re: никак не отпускает!

> Какой забавный выхлоп из /dev/random, все байты - printable...

Когда я не знаю, как решить задачу на перле, запускаю strings /dev/urandom | less и задумчиво листаю... а вы с вашими неправильными языками такой подсказки лишены!

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