egrep expressão regular para tempos superiores a cinco minutos

4

Eu tenho os seguintes formatos de hora em um arquivo de texto

       '1' equals one second.
    '5|01' equals five minutes and one seconds.
   '13|01' equals thirteen minutes and one seconds.
'21|12|01' equals 21 hours, 12 minutes, and 1 seconds.

Eu preciso egrep para qualquer momento durante cinco minutos. Eu estou usando o seguinte regex mas não funciona porque exclui tempos como 13|00 .

'[[:space:]0-9][[:space:]0-9][[:space:]|][[:space:]0-9][[:space:]6-9][|][0-9][0-9]'

Veja um exemplo:

 lite on       1
 lite on      01
 lite on    5|22
 lite on   23|14
 lite on 1|14|23
    
por user72055 01.10.2015 / 17:57

3 respostas

5

Ignorando os espaços (que você pode preencher mais tarde) e possíveis zeros à esquerda (da mesma forma), você está procurando combinar com qualquer um dos

[5-9]\|[0-9]+
[1-9][0-9]\|[0-9]+
[0-9]+\|[0-9]+\|[0-9]+

por vezes no intervalo

[5,10) minutes
[10,99) minutes
1+ hours

respectivamente.

Portanto, junte-os em um grupo de correspondência (...|...) com ancoragem suficiente no início e no final (para que você não corresponda a 14|59 ou 1|00|00 ).

Isso dá

grep -E 'on +([5-9]\|[0-9]+|[1-9][0-9]\|[0-9]+|[0-9]+\|[0-9]+\|[0-9]+) *$'

Podemos simplificar um pouco, porque os segundos são comuns a todos os três regexps:

grep -E 'on +([5-9]|[1-9][0-9]|[0-9]+\|[0-9]+)\|[0-9]+ *$'
    
por 01.10.2015 / 18:57
1

Isso deve funcionar:

grep -E '( [0-9]{1,2}\|[0-9]{1,2}\|[0-9][1-9] )|( [0-9][0-9]\|[0-9]{2,2} )|( [5-9]\|[0-9][1-9] )|( [6-9]\|[0-9][0-9] )' <file>

Basicamente você está criando 4 padrões encapsulados em () separados por | . os | agem como or no regex.

as {1,2} partes são 1-2 instances of preceding pattern então [0-9]{1,2} significa 1-2 instances of 0-9

Depois disso, criamos um caso de teste básico para todas as suas combinações de dígitos possíveis por meio da sintaxe or

    
por 01.10.2015 / 18:57
1

Não use grep . Expressões regulares são para padrões de correspondência, mas são realmente horríveis para valores correspondentes. Você provavelmente pode fazer isso, mas você está usando um martelo como uma chave de fenda. Tecnicamente funciona, mas é confuso e ineficiente.

Então, ao invés disso:

#!/usr/bin/env perl

use strict;
use warnings;

while (<DATA>) {
    my @numbers = m/(\d+)/g;
    my $seconds = pop(@numbers);
    $seconds += ( pop(@numbers) // 0 ) * 60;  #second digit minutes -> seconds
    $seconds
        += ( pop(@numbers) // 0 ) * 60 * 60;  #third digit, hours  -> seconds;
    print if $seconds > 300;
}

__DATA__
 lite on       1
 lite on      01
 lite on    5|22
 lite on   23|14
 lite on 1|14|23

Isto imprime:

 lite on    5|22
 lite on   23|14
 lite on 1|14|23

Você pode fazer isso com uma linha:

perl -ne 'for ( m/(\d+)/g ) { $t *= 60; $t += $_ }; print if $t > 300;'

Para pontos de bônus - isso lida com critérios de validação bastante arbitrários, sem muita dificuldade, e não requer nem de longe o máximo possível se você decidir ganhar um valor diferente um dia.

Mas o texto acima funciona de:

  • use m/(\d+)/g - como uma correspondência g , significa que ele seleciona instâncias repetidas de "um ou mais dígitos" em uma matriz. ( @numbers ou apenas como um iterador independente no loop for no segundo exemplo).
  • converte essa cadeia de dígitos em segundos, multiplicando por 60. (Isso não funcionará tão bem se você adicionar dias a ele!)
  • E depois testa esse número por ser maior que 300 - o que é 5 minutos em segundos.
por 01.10.2015 / 22:47