GoLang。 我想检查字符串中其内容的条件。 如何在GoLang中高效地做到这一点?

The string will only contain 0's or 4's. The string will start with 4. example: 444, 44, 40, 4400, 4440, etc. These all are valid strings but 404 is not valid. Currently, I am checking if 4 is present immediately after 0. I am not sure that this one is efficient one.

If you mean leading 4 and following 0.

use regexp

package main

import (
    "regexp"
)

func check(s string) bool {
    return regexp.MustCompile(`^4+0*$`).MatchString(s)
}

func main() {
    for _, tt := range []string{"444", "44", "40", "4400", "4440"} {
        if !check(tt) {
            panic("want true: " + tt)
        }
    }
    for _, tt := range []string{"404", "040"} {
        if check(tt) {
            panic("want false: " + tt)
        }
    }
}

non-regexp

package main

func check(s string) bool {
    i := 0
    r := []rune(s)
    for i = 0; i < len(r); i++ {
        if r[i] != '4' {
            break
        }
    }
    if i == 0 {
        return false
    }
    for ; i < len(r); i++ {
        if r[i] != '0' {
            return false
        }
    }
    return true
}

func main() {
    for _, tt := range []string{"444", "44", "40", "4400", "4440"} {
        if !check(tt) {
            panic("want true: " + tt)
        }
    }
    for _, tt := range []string{"404", "040"} {
        if check(tt) {
            panic("want false: " + tt)
        }
    }
}

faster version

func check(s string) bool {
    i, l := 0, len(s)
    for ; i < l; i++ {
        if s[i] != '4' {
            break
        }
    }
    if i == 0 {
        return false
    }
    for ; i < l; i++ {
        if s[i] != '0' {
            return false
        }
    }
    return true
}

No RegExp

package main

import (
        "fmt"
        "strings"
       )
func validate(str string) bool {

if strings.HasPrefix(str, "4") {
    for i:= 0; i < len(str)-1; i++ {
        if (str[i] == '0') && (str[i+1] == '4') {
            return false
        }
    }

}else { return false }

return true
}

func main() {

data := []string{"4", "44", "4400", "4440", "404", "004"}
      for _, val := range data {
            fmt.Println(validate(val))
      }
}

Output:

true
true
true
false
false

For example,

package main

import "fmt"

func isFourZero(s string) bool {
    i := 0
    var four bool
    for ; i < len(s) && s[i] == '4'; i++ {
        four = true
    }
    if four {
        if i >= len(s) {
            return true
        }
        var zero bool
        for ; i < len(s) && s[i] == '0'; i++ {
            zero = true
        }
        if zero {
            if i >= len(s) {
                return true
            }
        }
    }
    return false
}

func main() {
    tests := []struct{ s string }{
        {"444"}, {"44"}, {"40"}, {"4400"}, {"4440"}, {"404"}, {"004"},
    }
    for _, test := range tests {
        fmt.Printf("%q \t %t
", test.s, isFourZero(test.s))
    }
}

Output:

"444"    true
"44"     true
"40"     true
"4400"   true
"4440"   true
"404"    false
"004"    false

Since we care about speed, let's look at some benchmarks:

BenchmarkIsFourZeroPeterSO-4        10000000           201 ns/op
BenchmarkValidateYogeshDesai-4       5000000           347 ns/op
BenchmarkCheckMattn-4                2000000           602 ns/op

fourzero_test.go:

package main

import (
    "strings"
    "testing"
)

var tests = []struct{ s string }{
    {"444"}, {"44"}, {"40"}, {"4400"}, {"4440"}, {"404"}, {"004"},
}

func BenchmarkIsFourZeroPeterSO(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, test := range tests {
            isFourZero(test.s)
        }
    }
}

func BenchmarkValidateYogeshDesai(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, test := range tests {
            validate(test.s)
        }
    }
}

func BenchmarkCheckMattn(b *testing.B) {
    for i := 0; i < b.N; i++ {
        for _, test := range tests {
            check(test.s)
        }
    }
}

func isFourZero(s string) bool {
    i := 0
    var four bool
    for ; i < len(s) && s[i] == '4'; i++ {
        four = true
    }
    if four {
        if i >= len(s) {
            return true
        }
        var zero bool
        for ; i < len(s) && s[i] == '0'; i++ {
            zero = true
        }
        if zero {
            if i >= len(s) {
                return true
            }
        }
    }
    return false
}

func validate(str string) bool {
    if strings.HasPrefix(str, "4") {
        for i := 0; i < len(str)-1; i++ {
            if (str[i] == '0') && (str[i+1] == '4') {
                return false
            }
        }

    } else {
        return false
    }
    return true
}

func check(s string) bool {
    i := 0
    r := []rune(s)
    for i = 0; i < len(r); i++ {
        if r[i] != '4' {
            break
        }
    }
    if i == 0 {
        return false
    }
    for ; i < len(r); i++ {
        if r[i] != '0' {
            return false
        }
    }
    return true
}

The following is another implementation using only a single loop:

func yetAnotherValidation(s string) bool {
    //INVALID: if empty OR not started with '4'
    if len(s) == 0 || s[0] != '4' {
        return false
    }

    //INVALID: if len(s) > 2 AND contains "404"
    for k := 2; k < len(s); k++ {
        if s[k] == '4' && s[k-1] == '0' && s[k-2] == '4' {
            return false
        }
    }
    return true
}

Note:

  1. *404* (e.g. 404, 4404, 4040, ...) is INVALID.
  2. If s contains a character other than 0 or 4, the result will be undefined (depending on the position of that character). If you need to ensure whether the input only contains 0 or 4, then:

    func yetAnotherValidation2(s string) bool {
        //INVALID: if empty OR not started with '4'
        if len(s) == 0 || s[0] != '4' {
            return false
        }
    
        //INVALID: if second digit is not 0 or 4
        if len(s) > 1 && s[1] != '0' && s[1] != '4' {
            return false
        }
    
        //For len(s) > 2
        for k := 2; k < len(s); k++ {
            if s[k] == '4' && s[k-1] == '0' && s[k-2] == '4' {
                return false
            } else if s[k] != '0' && s[k] != '4' {
                //Neither 0 nor 4
                return false
            }
        }
        return true
    }
    

UPDATE:
Test and benchmark result:

=== RUN   TestValidate
444 true
44 true
40 true
4400 true
4440 true
404 false
004 false
--- PASS: TestValidate (0.00s)

BenchmarkYetAnotherValidation-4         50000000                38.5 ns/op
BenchmarkYetAnotherValidation2-4        30000000                45.6 ns/op
BenchmarkIsFourZero-4                   20000000                54.5 ns/op
BenchmarkCheckMattn-4                   10000000               144 ns/op
BenchmarkCheckMattnFast-4               30000000                50.2 ns/op