代码之家  ›  专栏  ›  技术社区  ›  Richard T

测试字符串是否为有效整数

  •  99
  • Richard T  · 技术社区  · 14 年前

    我正在尝试做一些足够常见的事情:解析shell脚本中的用户输入。如果用户提供了一个有效的整数,脚本将执行一项操作,如果无效,则执行其他操作。问题是,我没有找到一种简单(而且相当优雅)的方法来做到这一点——我不想一个接一个地把它拆开。

    我知道这一定很容易,但我不知道怎么做。我可以用十几种语言来做,但不能用bash!

    在我的研究中,我发现:

    Regular expression to test whether a string consists of a valid real number in base 10

    这里有一个关于regex的答案,但据我所知,这是C中的一个函数(以及其他函数)。尽管如此,它有一个看起来很好的答案,所以我尝试了与grep,但grep不知道如何处理它。我试过-p,它在我的盒子里意味着把它当作一个perl regexp-nada。破折号E(-E)也不起作用。也没有-f.

    只是要明确一点,我在尝试类似的东西,寻找任何输出——从那里,我将修改脚本,以利用我得到的任何东西。(注意,我希望当有效行重复时,不一致的输入不会返回任何内容。)

    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 回复  |  直到 6 年前
        1
  •  158
  •   Ian    8 年前
    [[ $var =~ ^-?[0-9]+$ ]]
    
    • 这个 ^ 指示输入模式的开始
    • 这个 - 是文字“-”
    • 这个 ? 表示前面的“0或1”( - )
    • 这个 + 指“前面的一个或多个”( [0-9] )
    • 这个 $ 指示输入模式的结尾

    所以regex匹配一个可选的 - (对于负数的情况),后跟一个或多个十进制数字。

    工具书类 :

        2
  •  58
  •   Olivia Stork Neil Kirk    6 年前

    真的。。。这里有很多好的解决方案!!在上述所有解决方案中,我同意@nortally使用 -eq 一个衬里是最酷的。

    我正在运行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.
    

    伊格纳西奥·瓦兹奎兹·艾布拉姆斯(IgnacioVazquezAbrams)提供的解决方案在解释之后也非常简洁(如果您喜欢regex的话)。但是,它不处理带 + 前缀,但它可以很容易地固定如下:

    [[ $var =~ ^[-+]?[0-9]+$ ]]
    
        3
  •  24
  •   tripleee    7 年前

    迟到的人来参加聚会。我非常惊讶,没有一个答案提到最简单、最快速、最便携的解决方案; case 声明。

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

    在比较之前修剪任何符号都有点像黑客,但这使得case语句的表达式简单得多。

        4
  •  9
  •   ephemient    14 年前

    对于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,回送第一个组(或匹配长度,如果没有),并返回成功/失败。这是旧的regex语法,因此 \ . -\? 意味着“也许” - [0-9]\+ 表示“一个或多个数字”,以及 $ 表示“字符串结束”。

    bash还支持扩展的globs,不过我不记得从哪个版本开始。

    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]) 表示“零位或多位数字”。

        5
  •  8
  •   JamesThomasMoon    8 年前

    我喜欢使用 -eq 测试,因为它基本上是一条直线。

    我自己的解决方案是使用参数扩展来丢弃所有的数字,看看是否还剩下什么。(我还在用3.0,还没用过 [[ expr 以前,但很高兴见到他们。)

    if [ "${INPUT_STRING//[0-9]}" = "" ]; then
      # yes, natural number
    else
      # no, has non-numeral chars
    fi
    
        6
  •  3
  •   hans    14 年前

    下面是对它的另一种处理(仅使用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
    
        7
  •  2
  •   Dennis Williamson    14 年前

    您可以去掉非数字并进行比较。下面是一个演示脚本:

    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
    
        8
  •  2
  •   Trebor Rude    10 年前

    对于我来说,最简单的解决方案是在 (()) 表达式,如:

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

    当然,只有当值为零对应用程序没有意义时,此解决方案才有效。在我的情况下,这是正确的,而且这比其他解决方案简单得多。

    正如注释中指出的,这会使您受到代码执行攻击: (( )) 运算符计算 VAR ,如 Arithmetic Evaluation 截面 the bash(1) man page .因此,当 var 是不确定的(当然,您也不应该使用任何其他形式的变量扩展)。

        9
  •  0
  •   knipwim    11 年前

    或与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
    
        10
  •  0
  •   JustinMT    7 年前

    加上伊格纳西奥·瓦兹奎兹·艾布拉姆斯的回答。这将允许+符号先于整数,并且允许任何数量的零作为小数点。例如,这将允许将+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
    
        11
  •  0
  •   Mike Q    6 年前

    为了搞笑,我粗略地计算了一组函数来做这个(是字符串、是int、是float、是alpha字符串或其他),但是有更有效(更少的代码)的方法来做这个:

    #!/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是一个int,但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 或用SED剥离)