20

What is an easy way in Golang to check if all characters in a string are upper case or lower case?

Also, how to handle a case where the string has punctuation?

See these examples:

package main

import (
    "fmt"
    "unicode"
)

func main() {
    s := "UPPERCASE"
    fmt.Println(s.IsUpper())  // Should print true

    s = "lowercase"
    fmt.Println(s.IsUpper())  // Should print false

    s = "lowercase"
    fmt.Println(s.IsLower())  // Should print true

    s = "I'M YELLING AT YOU!"
    fmt.Println(s.IsUpper())  // Should print true
}

Note: s.IsUpper() and s.IsLower() doesn't really exist, but would be nice to find an equivalent.

jersey bean
  • 3,321
  • 4
  • 28
  • 43

4 Answers4

35

You can of course compare the upper and lower cased strings in their entirety, or you can short-circuit the comparisons on the first failure, which would be more efficient when comparing long strings.

func IsUpper(s string) bool {
    for _, r := range s {
        if !unicode.IsUpper(r) && unicode.IsLetter(r) {
            return false
        }
    }
    return true
}

func IsLower(s string) bool {
    for _, r := range s {
        if !unicode.IsLower(r) && unicode.IsLetter(r) {
            return false
        }
    }
    return true
}
JimB
  • 104,193
  • 13
  • 262
  • 255
  • So I guess I'll state the obvious. I think your point is that using strings.ToUpper is a O(n) operation and is costly for really long strings (as n grows large). So your solution uses unicode.IsUpper and will short circuit as soon as your find a character which is not upper case. Good answer! – jersey bean Dec 12 '19 at 21:06
16

One solution is to use strings.ToUpper()/ToLower() and compare with the original string. This works for the punctuation case as well.

Here's the solution:

package main

import (
    "fmt"
    "strings"
)

func main() {
    s := "UPPERCASE"
    fmt.Println(strings.ToUpper(s) == s)

    s = "lowercase"
    fmt.Println(strings.ToUpper(s) == s)

    s = "lowercase"
    fmt.Println(strings.ToLower(s) == s)

    s = "I'M YELLING AT YOU!"
    fmt.Println(strings.ToUpper(s) == s)
}
jersey bean
  • 3,321
  • 4
  • 28
  • 43
0

A unicode.{IsUpper, Lower} and B strings.{ToUpper, Lower} both good

  • For the data composed of single bytes, A will be better than B
  • If the data byte is unsure then B is better than A: for example 中文a1
package main

import (
    "strings"
    "testing"
    "unicode"
)

func IsUpperU(s string) bool {
    for _, r := range s {
        if !unicode.IsUpper(r) && unicode.IsLetter(r) {
            return false
        }
    }
    return true
}

func IsUpper(s string) bool {
    return strings.ToUpper(s) == s
}

func IsLowerU(s string) bool {
    for _, r := range s {
        if !unicode.IsLower(r) && unicode.IsLetter(r) {
            return false
        }
    }
    return true
}

func IsLower(s string) bool {
    return strings.ToLower(s) == s
}

func TestIsUpper(t *testing.T) {
    for _, d := range []struct {
        actual   bool
        expected bool
    }{
        {IsUpperU("中文A1"), false}, // be careful!
        {IsUpper("中文A1"), true},

        {IsUpper("中文a1"), false},
        {IsUpperU("中文a1"), false},
    } {
        if d.actual != d.expected {
            t.Fatal()
        }
    }
}

func TestIsLower(t *testing.T) {
    for idx, d := range []struct {
        actual   bool
        expected bool
    }{
        {IsLowerU("中文a1"), false}, // be careful!
        {IsLower("中文a1"), true},

        {IsLower("中文A1"), false},
        {IsLowerU("中文A1"), false},
    } {
        if d.actual != d.expected {
            t.Fatal(idx)
        }
    }
}

go playground

Carson
  • 6,105
  • 2
  • 37
  • 45
0

No need for unicode (For English letters only):

func IsUpper(s string) bool {
    for _, charNumber := range s {
        if charNumber > 90 || charNumber < 65 {
            return false
        }
    }
    return true
}
    
func IsLower(s string) bool {
    for _, charNumber := range s {
        if charNumber > 122 || charNumber < 97 {
            return false
        }
    }
    return true
}