脚本之家,脚本语言编程技术及教程分享平台!
分类导航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服务器之家 - 脚本之家 - Golang - Golang 统计字符串中数字字母数量的实现方法

Golang 统计字符串中数字字母数量的实现方法

2022-10-12 11:51恋喵大鲤鱼 Golang

这篇文章主要介绍了Golang 统计字符串中数字字母数量,本文给出了两种从字符串获取数字与字母数量的方法,分别是ASCII 码值和正则表达式,需要的朋友可以参考下

1.需求说明

记录一下项目对用户 UGC 文本进行字数限制的具体实现。

不同的产品,出于种种原因,一般都会对用户输入的文本内容做字数限制。

  • 出于产品定位,比如 140 字符限制的 Twitter,让内容保持简洁凝练,易于阅读;
  • 出于用户的阅读体验,过多的文字会造成阅读疲劳,合适的字数能够提高阅读舒适度;
  • 出于技术与成本的考虑,不设上限的 UGC 内容会引发一些潜在的问题,比如增加存储的成本,降低检索效率等。

回到自己的项目,是一个用户发帖的业务场景。产品同学给到的要求是:

  • 帖子名称,限制在 25 个字;
  • 帖子正文,限制在 1500 字;
  • 关于字的说明:1 个汉字为一个字,一个 Emoji 表情相当于 1 个字,2 个数字/英文字母相当于 1 个字。

正常情况下,汉字,Emoji 字符,数字与英文字母都是单独的字符。这里 2 个数字/英文算作 1 个字,所以在计算字符串长度时,不能够使用 []rune 强转后来获取其长度,而是需要统计出数字与英文字母的数量,再加上其他字符数量,作为其长度。所以,要想实现产品同学的要求,关键是需要统计出用户输入文本中的数字与英文字母的数量。

2.实现

在 Golang,一般有两种方法。

2.1 ASCII 码值法

数字和英文字母的 ASCII 码值我们是知道的,通过对原字符串遍历,便可统计出数字/英文字母的数量。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// GetAlphanumericNumByASCII 根据 ASCII 码值获取字母数字数量。
func GetAlphanumericNumByASCII(s string) int {
    num := int(0)
    for i := 0; i < len(s); i++ {
        switch {
        case 48 <= s[i] && s[i] <= 57: // 数字
            fallthrough
        case 65 <= s[i] && s[i] <= 90: // 大写字母
            fallthrough
        case 97 <= s[i] && s[i] <= 122: // 小写字母
            num++
        default:
        }
    }
    return num
}
 
// 或者
// GetAlphanumericNumByASCIIV2 根据 ASCII 码值获取字母数字数量。
func GetAlphanumericNumByASCIIV2(s string) int {
    num := int(0)
    for _, c := range s {
        switch {
        case '0' <= c && c <= '9':
            fallthrough
        case 'a' <= c && c <= 'z':
            fallthrough
        case 'A' <= c && c <= 'Z':
            num++
        default:
        }
    }
    return num
}

2.2 正则表达式

我们可以利用 Golang 标准库包 regexp 获取指定表达式的字串数量。

?
1
2
3
4
5
6
// GetAlphanumericNumByRegExp 根据正则表达式获取字母数字数量。
func GetAlphanumericNumByRegExp(s string) int {
    rNum := regexp.MustCompile(`\d`)
    rLetter := regexp.MustCompile("[a-zA-Z]")
    return len(rNum.FindAllString(s, -1)) + len(rLetter.FindAllString(s, -1))
}

我们可以写个单测来验证下上面三个函数的正确性。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package string
import "testing"
func TestGetAlphanumericNumByASCII(t *testing.T) {
    type args struct {
        s string
    }
    tests := []struct {
        name string
        args args
        want int
    }{
        {
            name: "包含数字",
            args: args{"108条梁山好汉"},
            want: 3,
        },
        {
            name: "包含字母",
            args: args{"一百条梁山man"},
            want: 3,
        },
        {
            name: "包含数字与字母",
            args: args{"108条梁山man"},
            want: 6,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := GetAlphanumericNumByASCII(tt.args.s); got != tt.want {
                t.Errorf("GetAlphanumericNumByASCII() = %v, want %v", got, tt.want)
            }
        })
    }
}
func TestGetAlphanumericNumByASCIIV2(t *testing.T) {
    type args struct {
        s string
    }
    tests := []struct {
        name string
        args args
        want int
    }{
        {
            name: "包含数字",
            args: args{"108条梁山好汉"},
            want: 3,
        },
        {
            name: "包含字母",
            args: args{"一百条梁山man"},
            want: 3,
        },
        {
            name: "包含数字与字母",
            args: args{"108条梁山man"},
            want: 6,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := GetAlphanumericNumByASCIIV2(tt.args.s); got != tt.want {
                t.Errorf("GetAlphanumericNumByASCII() = %v, want %v", got, tt.want)
            }
        })
    }
}
func TestGetAlphanumericNumByRegExp(t *testing.T) {
    type args struct {
        s string
    }
    tests := []struct {
        name string
        args args
        want int
    }{
        {
            name: "包含数字",
            args: args{"108条梁山好汉"},
            want: 3,
        },
        {
            name: "包含字母",
            args: args{"一百条梁山man"},
            want: 3,
        },
        {
            name: "包含数字与字母",
            args: args{"108条梁山man"},
            want: 6,
        },
    }
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            if got := GetAlphanumericNumByRegExp(tt.args.s); got != tt.want {
                t.Errorf("GetAlphanumericNumByRegExp() = %v, want %v", got, tt.want)
            }
        })
    }
}

运行go test main/string命令,其中 main/string 为单元测试所在包的路径。输出如下:

ok      main/string     0.355s

验证无误。

3.性能对比

上面提到的两种方法都可以用来获取字符串中数字与英文字母的数量,那么我们应该采用哪一种方法呢?

功能上没有差别,那么我们来看下性能对比吧。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func BenchmarkGetAlphanumericNumByASCII(b *testing.B) {
    for n := 0; n < b.N; n++ {
        GetAlphanumericNumByASCII("108条梁山man")
    }
}
func BenchmarkGetAlphanumericNumByASCIIV2(b *testing.B) {
    for n := 0; n < b.N; n++ {
        GetAlphanumericNumByASCIIV2("108条梁山man")
    }
}
func BenchmarkGetAlphanumericNumByRegExp(b *testing.B) {
    for n := 0; n < b.N; n++ {
        GetAlphanumericNumByRegExp("108条梁山man")
    }
}

运行上面的基准测试,输出如下:

go test -bench=. -benchmem main/string

goos: windows
goarch: amd64
pkg: main/string
cpu: Intel(R) Core(TM) i7-9700 CPU @ 3.00GHz
BenchmarkGetAlphanumericNumByASCII-8            89540210                12.67 ns/op            0 B/op          0 allocs/op
BenchmarkGetAlphanumericNumByASCIIV2-8          63227778                19.11 ns/op            0 B/op          0 allocs/op
BenchmarkGetAlphanumericNumByRegExp-8             465954              2430 ns/op            1907 B/op         27 allocs/op
PASS
ok      main/string     3.965s

不测不知道,一测吓一跳。通过正则表达式的实现方式,代码虽然简洁,但是涉及多次内存配分,性能与 ASCII 码值法相比,差距非常之大,是 ASCII 码值法的 200 倍左右。所以从性能的考虑,推荐使用 ASCII 码值的方式获取数字字母数量。

ASCII 码值法有两种遍历方式,一种是按照字节遍历,一种是按照 rune 字符遍历。因为后者涉及 rune 字符的判断,所以性能会差一些。推荐使用按照字节遍历。

4.小结

本文给出了两种从字符串获取数字与字母数量的方法:

  • ASCII 码值。
  • 正则表达式。

出于性能的考虑,推荐使用 ASCII 码值法,并使用字节遍历的方式。

此外,本文给出的两种方法,三种实现方式,相关源码已放置开源库 go-huge-util,可 import 直接使用。

?
1
2
3
4
5
6
7
8
9
10
package main
import (
    "fmt"
    huge "github.com/dablelv/go-huge-util"
)
func main() {
    fmt.Println(huge.GetAlphanumericNumByASCII("108条梁山man"))    // 6
    fmt.Println(huge.GetAlphanumericNumByASCIIV2("108条梁山man"))      // 6
    fmt.Println(huge.GetAlphanumericNumByRegExp("108条梁山man"))   // 6
}

参考文献

golang统计出其中英文字母、空格、数字和其它字符的个数

到此这篇关于Golang 统计字符串中数字字母数量的文章就介绍到这了,更多相关Golang 统计字符串内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/K346K346/article/details/124936878

延伸 · 阅读

精彩推荐
  • GolangGo语言入门Go Web Fiber框架快速了解

    Go语言入门Go Web Fiber框架快速了解

    这篇文章主要为大家介绍了Go语言入门Go Web Fiber框架的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    宇宙之一粟9602022-10-09
  • Golangxorm根据数据库生成go model文件的操作

    xorm根据数据库生成go model文件的操作

    这篇文章主要介绍了xorm根据数据库生成go model文件的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    zgcbj10172021-03-08
  • GolangGo Java算法之单词搜索示例详解

    Go Java算法之单词搜索示例详解

    这篇文章主要为大家介绍了Go Java算法之单词搜索示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪...

    黄丫丫5522022-08-23
  • Golang何时使用 Rust 和何时使用 Golang?

    何时使用 Rust 和何时使用 Golang?

    Golang 和 Rust 之间有明显的区别。Golang 更加注重构建可以无限扩展的 Web API 和小型服务,尤其是在 goroutine 的强大支持下。Rust 也可以用于小型服务,但是从...

    今日头条7172020-10-21
  • Golanggolang API请求队列的实现

    golang API请求队列的实现

    本文主要介绍了golang API请求队列的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编...

    wang_yb5522022-09-28
  • GolangGolang空结构体struct{}用途,你知道吗

    Golang空结构体struct{}用途,你知道吗

    这篇文章主要介绍了Golang空结构体struct{}用途,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    罗奇正6342021-03-26
  • Golanggolang log4go的日志输出优化详解

    golang log4go的日志输出优化详解

    log4go源于google的一项log工程,但官方已经停止维护更新,下面这篇文章主要给大家介绍了关于golang log4go的日志输出优化的相关资料,文中通过示例代码介绍...

    ccpaging4042020-05-13
  • GolangGo每日一库之dateparse处理时间

    Go每日一库之dateparse处理时间

    不管什么时候,处理时间总是让人头疼的一件事情。今天要介绍的dateparse实现解析日期时间格式的字符串。具有一定的参考价值,感兴趣的小伙伴们可以参...

    darjun12112021-08-13