我一直在寻找一种扩展检查方法,涵盖各种有效的
numbers
,作为 value
、string
或 var reference
传递给方法,但大多数解决方案最多采用基本情况,例如纯整数或浮点数,或专用于格式化日期字符串(这不是我的要求范围)。
因此,我发布了一个解决方案的新问题,该问题验证传递的参数是否是常见的
decimal/int
、浮点(由 .
或 ,
、hex
分隔(以 0x
开头)
,包含 [0-9a-fA-F]
)、octal
(以 0
开头)、scientific notation/exponential
(格式为 52e-14
或 -2*10^8
)或自定义基数(如 <base>#n
,基数范围为 2到 64,允许数字 [0-9a-fA-F]
)。
这是我的解决方案,包括大量的测试用例,验证传递的参数是否是常见的十进制/整数、浮点(由 . 或 ,, 十六进制分隔(以 0x 开头,包含 [0-9a-fA-F ])、八进制(从 0 开始)、科学记数法/指数(形式为 52e-14 或 -2*10^8)或自定义基数(如 #n,基数范围为 2 到 64,允许数字[0-9a-fA-F]).
1000 次迭代所花费的时间比 HERE 中最快的 isint_Case() 方法慢约 6-7 倍,但涵盖并组合了更多可能遇到的数字或数字字符串类型,如 GNU.org - Shell 算术中所述。
方法 numTest() 使用 case + 扩展模式匹配(需要 shopt -s extglob),成功时返回 0,测试失败时返回 1。如果需要,注释掉的 printf 语句可以将结果传递到 stdout,例如: G。用于将连续结果捕获到 var 中(由于信息输出重定向到 /dev/tty,因此应该仅适用于值)。还添加了一个注释掉的内置基于 RegEx 的解决方案(速度慢约 4 秒 à 1000):
#! /bin/bash
shopt -s extglob
# Function accepts int (decimal), float, scientific (exponential), octal and hex numbers passed directly as values, expandable vars or var name references
numTest() {
# Check for and link val to var name reference
if declare -p "$1" &>/dev/null; then
local -n input="$1" #&>/dev/null
else
local var="$1"
local -n input=var
fi
## CASE PATTERN CHECK SOLUTION (~18s for 1000)
local isNum
case ${input#[-+]} in
# Octal, hex, decimal / scientific/exponential, as parsable by awk '{ print +$1, +$2 }' <<<'12345678e-6 -323*10^34'
@(+([0-9])|0[xX]+([0-9aAbBcCdDeEfF])|+([0-9])?(@([eE]|'^'|'*10^')?([+-])+([0-9]))) )
# Uncomment to print succeeded test values
#printf '%s' "IS INT: " >/dev/tty
#printf '%s\n' "$input"
return 0 ;;
# Explicit base 2 - 64
+([0-9])\#+([0-9aAbBcCdDeEfF]) )
local -i base=${input%%#*}
if [[ $base -ge 2 && $base -le 64 ]]; then
# Uncomment to print succeeded test values
#printf '%s' "IS INT: " >/dev/tty
#printf '%s\n' "$input"
return 0
fi ;;
# Float / scientific/exponential, as parsable by awk '{ print +$1, +$2 }' <<<'0.0314159e2 -323*10^34'
@(*([0-9])[.,]+([0-9])?(@([eE]|'^'|'*10^')?([+-])+([0-9])))|'NaN' )
# Uncomment to print succeeded test values
#printf '%s' "IS FLOAT: " >/dev/tty
#printf '%s\n' "$input"
return 0
;;
esac
# ## REGEX CHECK SOLUTION (~22s, ~4s slower for 1000)
# if [[ $input =~ ^[+-]?([0-9]+|0[xX][0-9aAbBcCdDeEfF]+|[0-9]+(([eE]|'^'|'*10^')[+-]?[0-9]+)?)$ ]]; then
## printf '%s' "IS INT: " >/dev/tty
## printf '%s\n' "$input"
# return 0
# elif [[ $input =~ ^[+-]?[0-9]*[.,][0-9]+(([eE]|'^'|'*10^')[+-]?[0-9]+)?$ || $input == 'NaN' ]]; then
## printf '%s' "IS FLOAT: " >/dev/tty
## printf '%s\n' "$input"
# return 0
# fi
# Uncomment to print failed test values
#printf '%s' "TEST_FAIL: " >/dev/tty
#printf '%s\n' "$input"
return 1
}
# Set to 1 to print test section captions
declare print=
## Declare test vars and array of test values
declare -i int_min
(( int_min=(1<<63) ))
declare -i int_max=$((int_min - 1))
## Success test var candidates
# Success test vars octal
declare -i convertedOctal=073
declare -i convertedOctal2=-073
declare -i convertedOctal3=+073
# Success test vars hex
declare -ai successTestsHex=( x1450 0x9532 0xA0b32 0x0 -0x0 -0X0 +0x0 +0X0 +0x08842 +0X08842 -0x05431 -0X05431 )
declare -a successTestsHexStr=( 'x1450' '0x9532' '0xA0b32' '0x0' '-0x0' '-0X0' '+0x0' '+0X0' '+0x08842' '+0X08842' '-0x05431' '-0X05431' )
# "success" - parsed as null at var assignment -> 0
declare -ai successTestsPseudoHex=( -x0x0 -x0x0 +x0x0 )
# Auto-converted ints to hex
declare -ai successTestsConvertedOctal=( $convertedOctal $convertedOctal2 $convertedOctal3 )
# Success test vars int
declare -ai successTestsInt=( 0 -0 +0 $int_max -$int_max +$int_max $int_min -$int_min +$int_min 1 -1 +1 9015 -9015 +9015 )
# Exponents (only possible as string)
declare -a successTestsIntStr=( '0' '-0' '+0' "$int_max" "-$int_max" "+$int_max" "$int_min" )
declare -a successTestsIntBaseStr=( '2#0' '2#1' '16#aF0' '16#AF0' '32#a' '32#A' '33#a' '33#A' '64#f' '64#F' '10#100' )
declare -a successTestsOctalStr=( '073' '-073' '+073' )
declare -a successTestsSciStr=( '0e0' '0E0' '087e-32' '087E-32' '-087e32' '-087E32' '-22048e-16' '-22048E-16' '+100e050' '+100E050' '+7e-25' '+7E-25' '+000e+00' '+000E+00' '-853e1' '-853E1' '-323^34' '-1^-3' '+166^8' '+89^-16' '-323*10^34' '-1*10^-3' '+166*10^8' '+89*10^-16' )
# Success test vars float
declare -a successTestsFloatStr=( 'NaN' 0.0 +0.0 0,0 -0.0 -0,0 1.34 +1.34 1,34 -1.34 -1,34 .494 ,494 +,494 -.494 -,494 '571.0014' '571,0014' '-571.0014' '-571,0014' )
declare -a successTestsSciFloatStr=( '13.62e06' '13.62E06' '-13.62e06' '-13.62E06' '+13.62e06' '+13.62E06' '-3.23^34' '-0.1^-3' '+16.6^8' '+8.9^-16' '-3,23*10^34' '-0,1*10^-3' '+16,6*10^8' '+8,9*10^-16' )
## Fail tests
# Fail test vars misc
declare -a failTestsMiscStr=( '+' '-' ' ' '_' 'E' 'e' 'a' 'A' 'x' 'X' '.' '-.' ',' '-,' )
declare empty=''
# Fail test vars hex
declare -a failTestsHexStr=( 'x0x0' '-x0x0' '+x0x0' '0x01x' '-0x01x' '+0x42x' '0x' 'x0' )
declare failHexStr="x0x0"
declare failHexStr1="-x0x0"
declare failHexStr2="+x0x0"
# Fail test vars int
declare -a failTestsIntStr=( "-$int_min" "+$int_min" '#1' '2_' 'i3x' ' 1 ' '22 ' ' 333' ' -1 ' '-02 ' ' -033 ' '- 0444' '+0-535' '+-5350' '-+5350' '--5350' '++5350' '0-' '0+' '0ate234' '1993-' '78844355+' )
declare -a failTestsIntBaseStr=( '0#0' '1#1' '0#1' '2#-1' '65#0' '65#a' '2#g' '2#G' )
# Fail test vars float
declare -a failTestsFloatStr=( 'gdXm0.18#' 'gdXm0,18#' '.0-' '.0+' '7.01-' '238.446+' '0.' '0,' '.0.' ',0,' 'df-93.87#' 'df-93,87#' ' -2.057,' ' 09.002 ' '-09.002 ' '+09,002 ' '1.01 ' '-1,01 ' ' +2.02' ' .2,02' ' -33.66 ' '-33,66 ' ' -5 .4 ' '-5 ,4' ' -0. 06' '-0, 06 ' 'e13.62e06' '13E.06' '-13.62Ee06' '-13.62eE06' '+13.62ee06' '+13.62EE06' )
## Run tests and measure elapsed time
declare start=$EPOCHREALTIME
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS DIRECT NUMBER INPUT"
for (( i=0; i<100; i++)); do
# Auto-converted octal -> decimal
numTest 0160
numTest -0160
numTest +0160
# Auto-converted (0x) hex -> decimal
numTest 0x3932
numTest 0xaF3e2
numTest -0x3932
numTest +0x3932
numTest 0
numTest -0
numTest +0
numTest $int_max
numTest -$int_max
numTest +$int_max
numTest $int_min
numTest 94316667
numTest -94316667
numTest +94316667
numTest 0e0
numTest 0E0
numTest 087e-32
numTest 087E-32
numTest -087e32
numTest -087E32
numTest -22048e-16
numTest -22048E-16
numTest +100e050
numTest +100E050
numTest +7e-25
numTest +7E-25
numTest +000e+00
numTest +000E+00
numTest -853e1
numTest -853E1
numTest '0'
numTest '-0'
numTest '+0'
numTest "$int_max"
numTest "-$int_max"
numTest "+$int_max"
numTest "$int_min"
numTest '99'
numTest '-2377'
numTest '+356'
numTest NaN
numTest 0.0
numTest -0.0
numTest +0.0
numTest 0,0
numTest -0,0
numTest +0,0
numTest .3930
numTest -.3930
numTest +.3930
numTest ,3930
numTest -,3930
numTest +,3930
numTest 261087.0532
numTest -261087.0532
numTest +261087.0532
numTest 261087,0532
numTest -261087,0532
numTest +261087,0532
# Auto-converted hex 0x
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS HEX"
_IFS="$IFS"; IFS=$'\n'
for param in "${successTestsHex[@]}"; do
numTest param
done
for param in "${successTestsPseudoHex[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS CONVERTED INT HEX"
for param in "${successTestsConvertedOctal[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS INT"
for param in "${successTestsInt[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS INT STR"
for param in "${successTestsIntStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS INT BASE STR"
for param in "${successTestsIntBaseStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS OCTAL STR"
for param in "${successTestsOctalStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS SCI INT STR"
for param in "${successTestsSciStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS FLOAT"
for param in "${successTestsFloatStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN SUCCESS TESTS SCI FLOAT STR"
for param in "${successTestsSciFloatStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS MISC"
numTest empty
for param in "${failTestsMiscStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS HEX"
numTest $failHexStr
numTest failHexStr
numTest $failHexStr1
numTest failHexStr1
numTest $failHexStr2
numTest failHexStr2
for param in "${failTestsHexStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS INT"
for param in "${failTestsIntStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS INT BASE STR"
for param in "${failTestsIntBaseStr[@]}"; do
numTest param
done
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS FLOAT"
for param in "${failTestsFloatStr[@]}"; do
numTest param
done
IFS="$_IFS"
[[ $print ]] && echo -e "\nBEGIN FAIL TESTS DIRECT NUMBER INPUT"
numTest
numTest ''
numTest '+'
numTest '+'
numTest '-'
numTest ' '
numTest '_'
numTest 'E'
numTest 'e'
numTest 'a'
numTest 'A'
numTest 'x'
numTest 'X'
numTest '0x'
numTest 'x0'
numTest 0x
numTest x0
numTest 0-
numTest 0+
numTest '0-'
numTest '0+'
numTest -$int_min
numTest +$int_min
numTest "-$int_min"
numTest "+$int_min"
numTest 0ate234
numTest '1993-'
numTest 1993-
numTest '78844355+'
numTest 78844355+
numTest .0-
numTest .0+
numTest 7.01-
numTest 238.446+
done
declare stop=$EPOCHREALTIME
declare startMsStr="${start##*.}"; "${startMsStr%%[!0]*}" 2>/dev/null
startMsStr="${startMsStr#*$_}"
declare stopMsStr="${stop##*.}"; "${stopMsStr%%[!0]*}" 2>/dev/null
stopMsStr="${stopMsStr#*$_}"
declare -i elapsedSeconds=$(( (${stop//.} - ${start//.}) / 1000000 ))
#declare -i elapsedMs=$( [[ $stopMsStr -gt $startMsStr ]] && echo $(( ( stopMsStr - startMsStr ) / 1000 )) || echo $(( ( startMsStr - stopMsStr ) / 1000 )) )
declare -i elapsedMs
if [[ $stopMsStr -gt $startMsStr ]]; then
elapsedMs=$(( ( stopMsStr - startMsStr ) / 1000 ))
else
elapsedMs=$(( ( startMsStr - stopMsStr ) / 1000 ))
fi
echo -e "ELAPSED TIME: $elapsedSeconds.${elapsedMs}s"
shopt -u extglob
提供的测试应涵盖大多数已检查的成功和失败案例,同时应用值传递和引用传递。
对于错误的十六进制 STR 值,failTestsHexStr() 也有失败的情况,在 INT var 赋值时发生伪“成功”,而 successTestsPseudoHex() 则在 var 赋值时演示伪成功结果,这是由于初步失败 - null -> 0 评价。
在打印结果时,_的输入出现了一个小问题,没有打印值,而是打印了引用名称,但是报告失败是可以的。