#!/bin/bash # Comparission of different method for determining if a string is a number # under bash (formated output, handling UTF8 and Latin1 output) # (C) 2020-2022 Felix Hauri - felix@f-hauri.ch # Licensed under terms of GPL v3. www.gnu.org isuint_Parm() { [ "$1" ] && [ -z "${1//[0-9]}" ] ;} isuint_Grep() { grep -qE '^[0-9]+$' <<<"$1"; } isuint_Bash() { set -- ${1//[+-]/.}; (( 10#$1 >= 0 )) 2> /dev/null ;} isuint_Case() { case $1 in ''|*[!0-9]*) return 1;;esac;} isuint_Regx() { [[ $1 =~ ^[0-9]+$ ]] ;} isint_Parm() { local chk=${1#[+-]}; [ "$chk" ] && [ -z "${chk//[0-9]}" ] ;} isint_Bash() { set -- "${1//[!+-]}" ${1#${1//[!+-]}}; (( ( 0 ${1:-+} 10#$2 ) ? 1:1 )) 2> /dev/null ;} isint_Shell() { [ -n "$1" ] && [ "$1" -eq "$1" ] 2>/dev/null;} isint_Case() { case ${1#[-+]} in ''|*[!0-9]*) return 1;;esac;} isint_Regx() { [[ $1 =~ ^[+-]?[0-9]+$ ]] ;} isnum_Parm() { local ck=${1#[+-]};ck=${ck/.};[ "$ck" ]&&[ -z "${ck//[0-9]}" ];} isnum_Regx() { [[ $1 =~ ^[+-]?([0-9]+([.][0-9]*)?|\.[0-9]+)$ ]] ;} isnum_Case() { case ${1#[-+]} in ''|.|*[!0-9.]*|*.*.*) return 1;; esac ;} testcases=( 0 1 42 -3 +42 +3. .9 3.14 +3.141 -31.4 '' . 3-3 3.1.4 3a a3 blah 'Good day!' ) printf '%-12s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s %4s\n' \ Value\\Func U{Prm,Grp,Bsh,Cse,Rgx} I{Prm,Shl,Bsh,Cse,Rgx} N{Prm,Cse,Rgx} for var in "${testcases[@]}"; do outstr='' for func in isuint_{Parm,Grep,Bash,Case,Regx} \ isint_{Parm,Shell,Bash,Case,Regx} isnum_{Parm,Case,Regx}; do if $func "$var" then outstr+=' ##' else outstr+=' --' fi done printf '%-11s %s\n' "|$var|" "$outstr" done testFunc() { local tests=1000 start=${EPOCHREALTIME//.} for ((;tests--;)) ;do "$1" "$3" done printf -v "$2" %u $((${EPOCHREALTIME//.}-start)) } case $LANG in *.utf8|*.UTF8|*.utf-8|*.UTF-8 ) mu=$'\u00B5';; * ) mu=$'\265';; esac strU8DiffLen() { local chLen=${#1} LANG=C LC_ALL=C;return $((${#1}-chLen));} percent(){ local p=00$((${1}00000/$2));printf -v "$3" %.2f%% ${p::-3}.${p: -3};} sortedTests() { local func NaNTime NumTime ftyp="$1" nTest="$2" tTest="$3" min i pct line local -a order=() shift 3 for func ;do testFunc "${ftyp}_$func" NaNTime "$tTest" testFunc "${ftyp}_$func" NumTime "$nTest" order[NaNTime+NumTime]=${ftyp}_$func\ $NumTime\ $NaNTime done printf '\n%-12s %14s %14s %16s %11s\n' Function Number NaN Total Rank min="${!order[*]}" min=${min%% *} for i in "${!order[@]}";do read -ra line <<<"${order[i]}" percent "$i" "$min" pct printf -v tnum %\'d "${line[1]}" strU8DiffLen "$tnum";dnum=$? printf -v tnan %\'d "${line[2]}" strU8DiffLen "$tnan";dnan=$? printf -v ttot %\'d "$i" strU8DiffLen "$ttot" printf "%-12s %*s${mu}s %*s${mu}s %*s${mu}s %10s\n" \ "${line[0]}" $((12+dnum)) "$tnum" $((12+dnan)) "$tnan" \ $((13+$?)) "$ttot" "$pct" done } sortedTests isuint "This is not a number." 31415926535897932384 \ Case Grep Parm Bash Regx ;\ sortedTests isint "This is not a number." 31415926535897932384 \ Case Parm Shell Bash Regx ;\ sortedTests isnum "This string is clearly not a number..." \ 3.141592653589793238462643383279502884 Case Parm Regx