如何在Golang中编写isNumeric函数?

I want to check if a string is numeric.

For example:

  • "abcd123" should return false.
  • "1.4" or "240" should return true.

I thought about using ParseInt and ParseFloat (from the strconv package), but am not sure if that is the right way.

I was thinking of using strconv ParseInt and ParseFloat but not sure if that is the right way.

Well, it's certainly a right way.

You don't need to use ParseInt, though. ParseFloat will do the job.

func isNumeric(s string) bool {
    _, err := strconv.ParseFloat(s, 64)
    return err == nil
}

See an example here: https://play.golang.org/p/D53HRS-KIL

You can use the strconv.Atoi function for check integer values, and the strconv.ParseFloat for float values. Below is an example:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    v1 := "14"

    if _, err := strconv.Atoi(v1); err == nil {
        fmt.Printf("%q looks like a number.
", v1)
    } else {
        fmt.Printf("%q is not a number.
", v1)
    }

    v2 := "1.4"
    if _, err := strconv.ParseFloat(v2, 64); err == nil {
        fmt.Printf("%q looks like a float.
", v2)
    } else {
        fmt.Printf("%q is not a float.
", v2)
    }
}

/* Output:
"14" looks like a number.
"1.4" looks like a float.
*/

You can check it on the Go Playground.

If you need to convert the string to a floating-point number strconv.ParseFloat is the first choice.
Here you just need to know that there is only "0123456789" and maximum one '.' in your string, here for me isNumDot is 12x faster than isNumeric, see:
Consider this (1.7 seconds) - optimized for performance:

func isNumDot(s string) bool {
    dotFound := false
    for _, v := range s {
        if v == '.' {
            if dotFound {
                return false
            }
            dotFound = true
        } else if v < '0' || v > '9' {
            return false
        }
    }
    return true
}

and this (21.7 seconds - doing more extra works "converts the string to a floating-point number"):

func isNumeric(s string) bool {
    _, err := strconv.ParseFloat(s, 64)
    return err == nil
}

try it:

package main

import (
    "fmt"
    "strconv"
    "time"
)

func isNumDot(s string) bool {
    dotFound := false
    for _, v := range s {
        if v == '.' {
            if dotFound {
                return false
            }
            dotFound = true
        } else if v < '0' || v > '9' {
            return false
        }
    }
    return true
}

func isNumeric(s string) bool {
    _, err := strconv.ParseFloat(s, 64)
    return err == nil
}

func main() {
    fmt.Println(isNumDot("240"))     //true
    fmt.Println(isNumDot("abcd123")) //false
    fmt.Println(isNumDot("0.4."))    //false
    fmt.Println(isNumDot("240 "))    //false
    benchmark(isNumDot)
    benchmark(isNumeric)
}

func benchmark(f func(string) bool) {
    var res bool
    t := time.Now()
    for i := 0; i < 100000000; i++ {
        res = f("a 240") || f("abcd123") || f("0.4.") || f("240 ")
    }
    fmt.Println(time.Since(t))
    fmt.Println(res)
}

output:

true
false
false
false
1.7822s
false
21.723s
false

All the answers are valid, but there's another option not yet suggested:

re := regexp.MustCompile(`^[0-9]+(\.[0-9]+)?$`)
isNum := re.Match([]byte("ab123"))

Playground demo

I tried to comment on Adrian's answer but I guess I don't have enough reputation points. Building on his excellent response, here is a variation using PCRE. Some brief explanation on the symbols if you are unfamiliar with regular expressions:

"^" matches the start of input (i.e. beginning of your string)

"$" matches the end of input (i.e. the end of your string)

"()" are grouping operators

"*" matches 0 or more

"+" matches 1 or more

"?" matches exactly 0 or 1

"\d" is a character class which represents the character values 0 through 9

So, the following would require at least a leading 0, permit "0.", and everything else that is normally identified as a floating point value. You can experiment with this a bit.

func isFloat(s string) bool {
    return regexp.MatchString(`^\d+(\.\d*)?$`, s)
}

Naturally, if you are calling this function to validate data, it should be cleaned:

str := strings.TrimSpace(someString)
if isFloat(str) {
  ...
}

That only works on ASCII characters. If you are dealing with UTF8 or another multi-byte character set (MBCS), it can be done with regexp but more work would be required and perhaps another approach altogether.