Permitir apenas regex de pontos flutuantes

0

Como posso verificar se uma string específica é um ponto flutuante? Estes são possíveis pontos flutuantes:

12.245
+.0009
3.11e33
43.1E11
2e-14

Isso é o que eu tentei:

grep "^[+\-\.0-9]" 
grep "^[+-]*[0-9]"
grep "^[+\-\.0-9]" 

E muitas outras coisas relacionadas, mas nenhuma filtrou nada. Quase todas as cordas passaram. Como eu resolveria esse problema?

    
por O'Niel 12.10.2017 / 21:41

3 respostas

4

grep -xE '[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?'

Com -x , estamos ancorando o regexp no início e na linha para que as linhas tenham que corresponder a esse padrão como um todo, em oposição ao padrão encontrado em qualquer lugar da linha.

Se você quisesse combinar com todos os suportados por POSIX / C strtod() como reconhecido por muitas implementações do utilitário printf , por exemplo:

r=[$(locale decimal_point)]
d=[0-9]
h=[0-9abcdefABCDEF]
grep -xE "[[:space:]]*[-+]?($d*$r?$d+([eE][-+]?$d+)?|\
0[xX]$h*$r?$h*([pP][-+]?$d+)?|\
[iI][nN][fF]([iI][nN][iI][tT][yY])?|\
[nN][aA][nN]|\
NAN\([abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0-9_]+\))"

Assim também incluindo coisas como 0x3f, 0xFP-4, -Infinity, NAN (qualquer que seja).

$ printf '%g\n' 0x3f 0xFp-4 -Infinity 'NAN(whatever)'
63
0.9375
-inf
nan
    
por 12.10.2017 / 22:03
0

Aviso: esta é uma solução imperfeita. A função Scalar::Util::looks_like_number() do Perl pode não ser a melhor opção de rotina para fazer isso. Consulte os comentários de StéphaneChazelas abaixo .

Vou deixar aqui se alguém quiser olhar para ele e pegar os coproc bits dele.

Em vez de tentar criar sua própria expressão regular para corresponder ao formato de número de ponto flutuante possível, use uma biblioteca que já a implementou:

perl -MScalar::Util -ne 'exit !Scalar::Util::looks_like_number($_)'

Como uma função shell bash :

is_number () {
    perl -MScalar::Util -ne 'exit !Scalar::Util::looks_like_number($_)' <<<"$1"
}

is_number hello && echo 'hello is a number'
is_number 1.234 && echo '1.234 is a number'

Como co-processo bash (para evitar iniciar um processo Perl para cada vez que você quiser testar um número):

coproc PERLIO=:raw perl -MScalar::Util -ne \
    'print Scalar::Util::looks_like_number($_) ? "Yes" : "No", "\n"'

while IFS= read -r -p 'Number please: ' possnum; do
    printf '%s\n' "$possnum" >&${COPROC[1]}
    read -u ${COPROC[0]}

    case "$REPLY" in
        Yes)    printf '%s is a number\n' "$possnum"       ;;
        No)     printf '%s is _not_ a number\n' "$possnum" ;;
    esac
done

kill "$COPROC_PID"

Ou combinando os dois:

coproc PERLIO=:raw perl -MScalar::Util -ne \
    'print Scalar::Util::looks_like_number($_) ? "Yes" : "No", "\n"'

is_number () {
    printf '%s\n' "$1" >&${COPROC[1]}

    local REPLY
    read -u ${COPROC[0]}

    [ "$REPLY" = 'Yes' ] && return 0

    return 1
}

while IFS= read -r -p 'Number please: ' possnum; do
    if is_number "$possnum"; then
        printf '%s is a number\n' "$possnum"
    else
        printf '%s is a _not_ a number\n' "$possnum"
    fi
done

kill "$COPROC_PID"
    
por 12.10.2017 / 22:36
0

Solução alternativa Python (para menos itens de entrada sofisticados):

Amostra input.txt file:

11
12.245
+.0009
---0
3.11e33
43.1E11
2e-14
t12
aaa
10.001
Script

check_float.py :

import sys

with open(sys.argv[1], 'r') as inp:
    f = 'No'
    for l in inp.read().splitlines():
        try:
            if float(l) and '.' in l: f = 'Yes'
        except ValueError:
            f = 'No'
        finally:
            print '{0} - {1}'.format(l, f)

Uso:

python check_float.py input.txt

A saída:

11 - No
12.245 - Yes
+.0009 - Yes
---0 - No
3.11e33 - Yes
43.1E11 - Yes
2e-14 - Yes
t12 - No
aaa - No
10.001 - Yes
    
por 12.10.2017 / 23:39