Проверить, является ли строка целым числом

Я пытаюсь сделать что-то достаточно распространенное: проанализировать пользовательский ввод в скрипте оболочки. Если пользователь предоставил допустимое целое число, скрипт делает одно, а если нет, он делает что-то еще. Проблема в том, что я не нашел простого (и достаточно элегантного) способа сделать это - я не хочу разбирать его на части.

Я знаю, это должно быть легко, но я не знаю как. Я мог бы сделать это на дюжине языков, но не Баш!

в моем исследовании я нашел это:

регулярное выражение для проверки, состоит ли строка из действительного действительного числа в базе 10

и там есть ответ, который говорит о регулярном выражении, но, насколько я знаю, это функция, доступная в C (среди других). Тем не менее, это было похоже на отличный ответ, поэтому я попробовал его с grep, но grep не знал, что с ним делать. Я попробовал-P, который на моей коробке означает рассматривать его как Perl regexp-nada. Тире E (- E) тоже не сработало. И не - Ф.

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

snafu=$(echo "" | grep -E "/^[-+]?(?:\.[0-9]+|(?:0|[1-9][0-9]*)(?:\.[0-9]*)?)$/")
if [ -z "$snafu" ] ;
then
   echo "Not an integer - nothing back from the grep"
else
   echo "Integer."
fi

кто-нибудь, пожалуйста, проиллюстрируйте, как это легче всего сделать?

честно говоря, это короткий тест, на мой взгляд. У него должен быть такой флаг

if [ -I "string" ] ;
then
   echo "String is a valid integer."
else
   echo "String is not a valid integer."
fi

11 ответов


[[ $var =~ ^-?[0-9]+$ ]]
  • на ^ указывает начало входного шаблона
  • на - - это литерал "-"
  • на ? означает "0 или 1 предыдущего (-)"
  • на + означает " 1 или более из предыдущих ([0-9])"
  • на $ указывает конец входного шаблона

таким образом, регулярное выражение соответствует необязательному - (для отрицательных чисел), за которым следует одна или несколько десятичных десятичные знаки.

ссылки:


Вау... здесь так много хороших решений!! Из всех вышеперечисленных решений я согласен с @nortally, что использование -eq один лайнер-самый крутой.

я запускаю GNU bash, версия 4.1.5 (Debian). Я также проверил это на ksh (SunSO 5.10).

вот моя версия проверки, если является целым числом или нет:

if [ "" -eq "" ] 2>/dev/null
then
    echo " is an integer !!"
else
    echo "ERROR: first parameter must be an integer."
    echo $USAGE
    exit 1
fi

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

результаты:

$ int_check.sh 123
123 is an integer !!

$ int_check.sh 123+
ERROR: first parameter must be an integer.

$ int_check.sh -123
-123 is an integer !!

$ int_check.sh +30
+30 is an integer !!

$ int_check.sh -123c
ERROR: first parameter must be an integer.

$ int_check.sh 123c
ERROR: first parameter must be an integer.

$ int_check.sh c123
ERROR: first parameter must be an integer.

решение, предоставленное Игнасио Васкес-Абрамсом, также было очень аккуратным (если вам нравится регулярное выражение) после его объяснения. Однако он не обрабатывает положительные числа с помощью + префикс, но его можно легко исправить, как показано ниже:

[[ $var =~ ^[-+]?[0-9]+$ ]]

опоздавший на вечеринку здесь. Я очень удивлен, что ни один из ответов не упоминает самое простое, быстрое и портативное решение;case заявление.

case ${variable#[-+]} in
  *[!0-9]* | '') echo Not a number ;;
  * ) echo Valid number ;;
esac

обрезка любого знака перед сравнением кажется немного взломанной, но это делает выражение для оператора case намного проще.


для переносимости в pre-Bash 3.1 (когда =~ был введен тест), используйте expr.

if expr "$string" : '-\?[0-9]\+$' >/dev/null
then
  echo "String is a valid integer."
else
  echo "String is not a valid integer."
fi

expr STRING : REGEX ищет регулярное выражение, закрепленное в начале строки, повторяя первую группу (или длину совпадения, если нет) и возвращая успех/сбой. Это старый синтаксис regex, следовательно, избыток \. -\? означает "может быть -", [0-9]\+ означает "одна или несколько цифр" и $ означает "конец строки".

Bash также поддерживает расширенные глобусы, хотя я не помню с какой версии начиная.

shopt -s extglob
case "$string" of
    @(-|)[0-9]*([0-9]))
        echo "String is a valid integer." ;;
    *)
        echo "String is not a valid integer." ;;
esac

# equivalently, [[ $string = @(-|)[0-9]*([0-9])) ]]

@(-|) значит "- или ничего", [0-9] означает "цифра", и *([0-9]) означает "ноль или более цифр".


мне нравится решение с помощью -eq тест, потому что это в основном однострочный.

моим собственным решением было использовать расширение параметров, чтобы выбросить все цифры и посмотреть, осталось ли что-нибудь. (Я все еще использую 3.0, не использовал [[ или expr раньше, но рад встретиться с ними.)

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi

вот еще один пример (только с помощью команды test builtin и ее кода возврата):

function is_int() { return $(test "$@" -eq "$@" > /dev/null 2>&1); } 

input="-123"

if $(is_int "${input}");
then
   echo "Input: ${input}"
   echo "Integer: $[${input}]"
else
   echo "Not an integer: ${input}"
fi

вы можете удалить не-цифры и сделать сравнение. Вот демо-скрипт:

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09"
do
    match=${num//[^[:digit:]]}    # strip non-digits
    match=${match#0*}             # strip leading zeros
    echo -en "$num\t$match\t"
    case $num in
        $match|-$match)    echo "Integer";;
                     *)    echo "Not integer";;
    esac
done

вот как выглядит тестовый вывод:

44      44      Integer
-44     44      Integer
44-     44      Not integer
4-4     44      Not integer
a4      4       Not integer
4a      4       Not integer
.4      4       Not integer
4.4     44      Not integer
-4.4    44      Not integer
09      9       Not integer

для меня самым простым решением было использовать переменную внутри (()) выражение, как так:

if ((VAR > 0))
then
  echo "$VAR is a positive integer."
fi

конечно, это решение допустимо только в том случае, если значение нуля не имеет смысла для вашего приложения. Это случилось в моем случае, и это намного проще, чем другие решения.

как указал в комментариях, это может сделать вас объектом атаки выполнение кода:(( )) оператор оценивает VAR, как говорится в из The bash (1) man page. Поэтому вы не должны использовать эту технику, когда источник содержимого VAR неопределенно (и вы не должны использовать любую другую форму переменного расширения, конечно).


или с помощью sed:

   test -z $(echo "2000" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # integer

   test -z $(echo "ab12" | sed s/[0-9]//g) && echo "integer" || echo "no integer"
   # no integer

добавление к ответу от Игнасио Васкес-Абрамс. Это позволит знаку + предшествовать целому числу, и это позволит любому количеству нулей в качестве десятичных точек. Например, это позволит считать + 45.00000000 целым числом.
Однако, 1 $должен быть отформатирован, чтобы содержать десятичную точку. 45 здесь не считается целым числом,но 45.0.

if [[  =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
elif [[  =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi

для смеха я примерно просто быстро разработал набор функций для этого (is_string, is_int, is_float, is alpha string или другой), но есть более эффективные (меньше кода) способы сделать это:

#!/bin/bash

function strindex() {
    x="${1%%*}"
    if [[ "$x" = "" ]] ;then
        true
    else
        if [ "${#x}" -gt 0 ] ;then
            false
        else
            true
        fi
    fi
}

function is_int() {
    if is_empty "" ;then
        false
        return
    fi
    tmp=$(echo "" | sed 's/[^0-9]*//g')
    if [[ $tmp == "" ]] || [[ "-${tmp}" == "" ]] ; then
        #echo "INT () tmp=$tmp"
        true
    else
        #echo "NOT INT () tmp=$tmp"
        false
    fi
}

function is_float() {
    if is_empty "" ;then
        false
        return
    fi
    if ! strindex "" "-" ; then
        false
        return
    fi
    tmp=$(echo "" | sed 's/[^a-z. ]*//g')
    if [[ $tmp =~ "." ]] ; then
        #echo "FLOAT  () tmp=$tmp"
        true
    else
        #echo "NOT FLOAT  () tmp=$tmp"
        false
    fi
}

function is_strict_string() {
    if is_empty "" ;then
        false
        return
    fi
    if [[ "" =~ ^[A-Za-z]+$ ]]; then
        #echo "STRICT STRING ()"
        true
    else
        #echo "NOT STRICT STRING ()"
        false
    fi
}

function is_string() {
    if is_empty "" || is_int "" || is_float "" || is_strict_string "" ;then
        false
        return
    fi
    if [ ! -z "" ] ;then
        true
        return
    fi
    false
}
function is_empty() {
    if [ -z "${1// }" ] ;then
        true
    else
        false
    fi
}

выполните некоторые тесты здесь, я определил, что -44 является int, но 44 - не является etc.. :

for num in "44" "-44" "44-" "4-4" "a4" "4a" ".4" "4.4" "-4.4" "09" "hello" "h3llo!" "!!" " " "" ; do
    if is_int "$num" ;then
        echo "INT = $num"

    elif is_float "$num" ;then
        echo "FLOAT = $num"

    elif is_string "$num" ; then
        echo "STRING = $num"

    elif is_strict_string "$num" ; then
        echo "STRICT STRING = $num"
    else
        echo "OTHER = $num"
    fi
done

выход:

INT = 44
INT = -44
STRING = 44-
STRING = 4-4
STRING = a4
STRING = 4a
FLOAT = .4
FLOAT = 4.4
FLOAT = -4.4
INT = 09
STRICT STRING = hello
STRING = h3llo!
STRING = !!
OTHER =  
OTHER = 

Примечание: ведущие 0 могут вывести что-то еще при добавлении чисел, таких как восьмеричные, поэтому было бы лучше их удалить, если вы намеревайтесь рассматривать ' 09 ' как int (что я делаю) (например,expr 09 + 0 или полоса с sed)