如何在Golang中编写isNumeric函数?

huangapple go评论89阅读模式
英文:

How to write isNumeric function in Golang?

问题

我想检查一个字符串是否为数字。

例如:

  • "abcd123" 应返回 false
  • "1.4""240" 应返回 true

我考虑使用 ParseIntParseFloat(来自 strconv 包),但不确定是否是正确的方法。

英文:

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.

答案1

得分: 22

嗯,这肯定是一种正确的方法。

不过,你不需要使用ParseInt,使用ParseFloat就可以了。

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

在这里可以看到一个示例:https://play.golang.org/p/D53HRS-KIL

英文:

> 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

答案2

得分: 6

如果您需要将字符串转换为浮点数,strconv.ParseFloat是首选方法。
在这里,您只需要知道字符串中只有"0123456789"和最多一个'.',对我来说,isNumDotisNumeric快12倍,如下所示:
考虑以下优化性能的代码(1.7秒):

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
}

和这个(21.7秒 - 做更多额外的工作“将字符串转换为浮点数”):

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

尝试一下:

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)
}

输出:

true
false
false
false
1.7822s
false
21.723s
false

使用基准测试(isNumDotisNumeric更快):

BenchmarkIsNumDot-8     34117197        31.2 ns/op       0 B/op       0 allocs/op
BenchmarkIsNumeric-8     1931089       630 ns/op     192 B/op       4 allocs/op

// r = isNumDot("2.22")
BenchmarkIsNumDot-8     102849996        11.4 ns/op       0 B/op       0 allocs/op
BenchmarkIsNumeric-8    21994874        48.5 ns/op       0 B/op       0 allocs/op

// r = isNumDot("a 240")
BenchmarkIsNumDot-8     256610877         4.58 ns/op       0 B/op       0 allocs/op
BenchmarkIsNumeric-8     8962381       140 ns/op      48 B/op       1 allocs/op

基准测试代码:

package main

import (
    "testing";
)

var r bool

func BenchmarkIsNumDot(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = isNumDot("a 240") || isNumDot("abcd123") || isNumDot("0.4.") || isNumDot("240 ")
    }
}

func BenchmarkIsNumeric(b *testing.B) {
    for i := 0; i < b.N; i++ {
        r = isNumeric("a 240") || isNumeric("abcd123") || isNumeric("0.4.") || isNumeric("240 ")
    }
}
英文:

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 &quot;0123456789&quot; and maximum one &#39;.&#39; 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 == &#39;.&#39; {
			if dotFound {
				return false
			}
			dotFound = true
		} else if v &lt; &#39;0&#39; || v &gt; &#39;9&#39; {
			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 (
	&quot;fmt&quot;
	&quot;strconv&quot;
	&quot;time&quot;
)

func isNumDot(s string) bool {
	dotFound := false
	for _, v := range s {
		if v == &#39;.&#39; {
			if dotFound {
				return false
			}
			dotFound = true
		} else if v &lt; &#39;0&#39; || v &gt; &#39;9&#39; {
			return false
		}
	}
	return true
}

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

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

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

output:

true
false
false
false
1.7822s
false
21.723s
false

Using the benchmark (isNumDot is faster than isNumeric):

BenchmarkIsNumDot-8    	34117197	        31.2 ns/op	       0 B/op	       0 allocs/op
BenchmarkIsNumeric-8   	 1931089	       630 ns/op	     192 B/op	       4 allocs/op

// r = isNumDot(&quot;2.22&quot;)
BenchmarkIsNumDot-8    	102849996	        11.4 ns/op	       0 B/op	       0 allocs/op
BenchmarkIsNumeric-8   	21994874	        48.5 ns/op	       0 B/op	       0 allocs/op

// r = isNumDot(&quot;a 240&quot;)
BenchmarkIsNumDot-8    	256610877	         4.58 ns/op	       0 B/op	       0 allocs/op
BenchmarkIsNumeric-8   	 8962381	       140 ns/op	      48 B/op	       1 allocs/op

The benchmark:

package main

import (
	&quot;testing&quot;
)

var r bool

func BenchmarkIsNumDot(b *testing.B) {
	for i := 0; i &lt; b.N; i++ {
		r = isNumDot(&quot;a 240&quot;) || isNumDot(&quot;abcd123&quot;) || isNumDot(&quot;0.4.&quot;) || isNumDot(&quot;240 &quot;)
	}
}

func BenchmarkIsNumeric(b *testing.B) {
	for i := 0; i &lt; b.N; i++ {
		r = isNumeric(&quot;a 240&quot;) || isNumeric(&quot;abcd123&quot;) || isNumeric(&quot;0.4.&quot;) || isNumeric(&quot;240 &quot;)
	}
}

答案3

得分: 3

我试图评论Adrian的答案,但我猜我没有足够的声望点数。在他出色的回答基础上,这里是使用PCRE的一个变体。如果你对正则表达式不熟悉,以下是一些符号的简要解释:

  • "^" 匹配输入的开头(即你的字符串的开头)
  • "$" 匹配输入的结尾(即你的字符串的结尾)
  • "()" 是分组操作符
  • "*" 匹配0个或多个
  • "+" 匹配1个或多个
  • "?" 匹配0个或1个
  • "\d" 是一个字符类,表示字符值从0到9

因此,以下内容要求至少有一个前导0,允许"0.",以及通常被识别为浮点数值的其他内容。你可以稍微尝试一下。

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

当然,如果你调用这个函数来验证数据,应该先清理一下:

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

这只适用于ASCII字符。如果你处理的是UTF8或其他多字节字符集(MBCS),可以使用正则表达式完成,但可能需要更多的工作,或者完全采用另一种方法。

英文:

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.

答案4

得分: 2

你可以使用strconv.Atoi函数来检查整数值,使用strconv.ParseFloat函数来检查浮点数值。以下是一个示例:

package main

import (
	"fmt"
	"strconv"
)

func main() {
	v1 := "14"
	
	if _, err := strconv.Atoi(v1); err == nil {
	    fmt.Printf("%q 看起来像一个数字。\n", v1)
	} else {
	    fmt.Printf("%q 不是一个数字。\n", v1)
	}
	
	v2 := "1.4"
	if _, err := strconv.ParseFloat(v2, 64); err == nil {
	    fmt.Printf("%q 看起来像一个浮点数。\n", v2)
	} else {
	    fmt.Printf("%q 不是一个浮点数。\n", v2)
	}
}

/* 输出:
"14" 看起来像一个数字。
"1.4" 看起来像一个浮点数。
*/

你可以在Go Playground上运行它。

英文:

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 (
	&quot;fmt&quot;
	&quot;strconv&quot;
)

func main() {
	v1 := &quot;14&quot;
	
	if _, err := strconv.Atoi(v1); err == nil {
	    fmt.Printf(&quot;%q looks like a number.\n&quot;, v1)
	} else {
	    fmt.Printf(&quot;%q is not a number.\n&quot;, v1)
	}
	
	v2 := &quot;1.4&quot;
	if _, err := strconv.ParseFloat(v2, 64); err == nil {
	    fmt.Printf(&quot;%q looks like a float.\n&quot;, v2)
	} else {
	    fmt.Printf(&quot;%q is not a float.\n&quot;, v2)
	}
}

/* Output:
&quot;14&quot; looks like a number.
&quot;1.4&quot; looks like a float.
*/

You can check it on the Go Playground.

答案5

得分: 2

所有的答案都是有效的,但还有另一种尚未提出的选项:

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

Playground演示

英文:

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

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

Playground demo

答案6

得分: 0

我今天在一个高吞吐量系统中遇到了这个问题,并对这三个建议进行了基准测试。结果如下:

BenchmarkNumDot-4: 657966132: 18.2 ns/op
BenchmarkNumeric-4: 49575919: 226 ns/op
BenchmarkRegexp-4: 18817201: 628 ns/op

以下是代码,因为 playground 不支持基准测试。

package main

import (
	"regexp"
	"strconv"
	"testing"
)


func BenchmarkNumDot(b *testing.B) {
    for i := 0; i < b.N; i++ {
        isNumDot("abc")
	isNumDot("123")
	isNumDot("12.34")
	isNumDot("1.2.3.4")
    }
}

func BenchmarkNumeric(b *testing.B) {
    for i := 0; i < b.N; i++ {
        isNumeric("abc")
	isNumeric("123")
	isNumeric("12.34")
	isNumeric("1.2.3.4")
    }
}

func BenchmarkRegexp(b *testing.B) {
    re := regexp.MustCompile(`^[0-9]+(\.[0-9]+)?$`)

    for i := 0; i < b.N; i++ {
        isNumReg("abc", re)
        isNumReg("123", re)
        isNumReg("12.34", re)
        isNumReg("1.2.3.4", re)
    }
}

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 isNumReg(s string, re *regexp.Regexp) bool {
	return re.Match([]byte(s))
}

希望对你有帮助!

英文:

I hit this in a high-throughput system today and did a benchmark of the three suggestions. Results:

BenchmarkNumDot-4: 657966132: 18.2 ns/op
BenchmarkNumeric-4: 49575919: 226 ns/op
BenchmarkRegexp-4: 18817201: 628 ns/op

Code follows since the playground does not support benchmarking.

package main
import (
&quot;regexp&quot;
&quot;strconv&quot;
&quot;testing&quot;
)
func BenchmarkNumDot(b *testing.B) {
for i := 0; i &lt; b.N; i++ {
isNumDot(&quot;abc&quot;)
isNumDot(&quot;123&quot;)
isNumDot(&quot;12.34&quot;)
isNumDot(&quot;1.2.3.4&quot;)
}
}
func BenchmarkNumeric(b *testing.B) {
for i := 0; i &lt; b.N; i++ {
isNumeric(&quot;abc&quot;)
isNumeric(&quot;123&quot;)
isNumeric(&quot;12.34&quot;)
isNumeric(&quot;1.2.3.4&quot;)
}
}
func BenchmarkRegexp(b *testing.B) {
re := regexp.MustCompile(`^[0-9]+(\.[0-9]+)?$`)
for i := 0; i &lt; b.N; i++ {
isNumReg(&quot;abc&quot;, re)
isNumReg(&quot;123&quot;, re)
isNumReg(&quot;12.34&quot;, re)
isNumReg(&quot;1.2.3.4&quot;, re)
}
}
func isNumDot(s string) bool {
dotFound := false
for _, v := range s {
if v == &#39;.&#39; {
if dotFound {
return false
}
dotFound = true
} else if v &lt; &#39;0&#39; || v &gt; &#39;9&#39; {
return false
}
}
return true
}
func isNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
func isNumReg(s string, re *regexp.Regexp) bool {
return re.Match([]byte(s))
}

huangapple
  • 本文由 发表于 2017年8月15日 11:40:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/45686163.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定