Проверьте, является ли строка допустимым целым числом - PullRequest
103 голосов
/ 05 февраля 2010

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

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

В своем исследовании я нашел следующее:

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

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

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

snafu=$(echo "$2" | 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 ]

166 голосов
/ 06 февраля 2010
[[ $var =~ ^-?[0-9]+$ ]]
  • ^ указывает начало входного паттерна
  • - является буквальным "-"
  • ? означает «0 или 1 из предыдущего (-
  • + означает «1 или более из предыдущих ([0-9]
  • $ указывает на конец входного паттерна

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

Ссылки

59 голосов
/ 01 октября 2013

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

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

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

if [ "$1" -eq "$1" ] 2>/dev/null
then
    echo "$1 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]+$ ]]
25 голосов
/ 04 сентября 2013

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

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

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

9 голосов
/ 23 июня 2010

Мне нравится решение с использованием теста -eq, потому что оно в основном однострочное.

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

if [ "${INPUT_STRING//[0-9]}" = "" ]; then
  # yes, natural number
else
  # no, has non-numeral chars
fi
9 голосов
/ 06 февраля 2010

Для переносимости в 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, привязанного в начале STRING, повторяяпервая группа (или длина совпадения, если нет) и возвращение успеха / неудачи.Это старый синтаксис регулярных выражений, отсюда и избыток \.-\? означает «возможно -», [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]) означает «ноль или более цифр».

3 голосов
/ 06 февраля 2010

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

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
2 голосов
/ 21 марта 2014

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

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

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

Как указано в комментариях, это может привести к атаке выполнения кода: оператор (( )) оценивает VAR, как указано в разделе Arithmetic Evaluation справочной страницы bash (1). . Следовательно, вы не должны использовать эту технику, когда источник содержимого VAR является неопределенным (и, конечно, вы не должны использовать ЛЮБУЮ другую форму расширения переменных).

2 голосов
/ 06 февраля 2010

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

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
0 голосов
/ 17 мая 2018

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

#!/bin/bash

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

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

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

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

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

Пройдя здесь несколько тестов, я определил, что -44 - это целое число, а 44 - это не так ...:

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 или strip с седом)

0 голосов
/ 23 июля 2017

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

.
if [[ $1 =~ ^-?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
elif [[ $1 =~ ^\+?[0-9]+.?[0]+$ ]]; then
    echo "yes, this is an integer"
else
    echo "no, this is not an integer"
fi
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...