热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Golang如何统计字符串中数字字母的数量

今天小编给大家分享一下Golang如何统计字符串中数字字母的数量的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享

今天小编给大家分享一下Golang如何统计字符串中数字字母的数量的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

    1.需求说明

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

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

    • 出于产品定位,比如 140 字符限制的 Twitter,让内容保持简洁凝练,易于阅读;

    • 出于用户的阅读体验,过多的文字会造成阅读疲劳,合适的字数能够提高阅读舒适度;

    • 出于技术与成本的考虑,不设上限的 UGC 内容会引发一些潜在的问题,比如增加存储的成本,降低检索效率等。

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

    • 帖子名称,限制在 25 个字;

    • 帖子正文,限制在 1500 字;

    • 关于字的说明:1 个汉字为一个字,一个 Emoji 表情相当于 1 个字,2 个数字/英文字母相当于 1 个字。

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

    2.实现

    在 Golang,一般有两种方法。

    2.1 ASCII 码值法

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

    // 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 &#39;0&#39; <= c && c <= &#39;9&#39;:
    			fallthrough
    		case &#39;a&#39; <= c && c <= &#39;z&#39;:
    			fallthrough
    		case &#39;A&#39; <= c && c <= &#39;Z&#39;:
    			num++
    		default:
    		}
    	}
    	return num
    }

    2.2 正则表达式

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

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

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

    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.性能对比

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

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

    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 直接使用。

    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如何统计字符串中数字字母的数量”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程笔记行业资讯频道。


    推荐阅读
    • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
    • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
    • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
    • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
    • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
    • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
    • 本文介绍了基于c语言的mcs51单片机定时器计数器的应用教程,包括定时器的设置和计数方法,以及中断函数的使用。同时介绍了定时器应用的举例,包括定时器中断函数的编写和频率值的计算方法。主函数中设置了T0模式和T1计数的初值,并开启了T0和T1的中断,最后启动了CPU中断。 ... [详细]
    • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
    • C# 7.0 新特性:基于Tuple的“多”返回值方法
      本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
    • Oracle分析函数first_value()和last_value()的用法及原理
      本文介绍了Oracle分析函数first_value()和last_value()的用法和原理,以及在查询销售记录日期和部门中的应用。通过示例和解释,详细说明了first_value()和last_value()的功能和不同之处。同时,对于last_value()的结果出现不一样的情况进行了解释,并提供了理解last_value()默认统计范围的方法。该文对于使用Oracle分析函数的开发人员和数据库管理员具有参考价值。 ... [详细]
    • 不同优化算法的比较分析及实验验证
      本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
    • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
    • Python瓦片图下载、合并、绘图、标记的代码示例
      本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
    • [大整数乘法] java代码实现
      本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
    • OO第一单元自白:简单多项式导函数的设计与bug分析
      本文介绍了作者在学习OO的第一次作业中所遇到的问题及其解决方案。作者通过建立Multinomial和Monomial两个类来实现多项式和单项式,并通过append方法将单项式组合为多项式,并在此过程中合并同类项。作者还介绍了单项式和多项式的求导方法,并解释了如何利用正则表达式提取各个单项式并进行求导。同时,作者还对自己在输入合法性判断上的不足进行了bug分析,指出了自己在处理指数情况时出现的问题,并总结了被hack的原因。 ... [详细]
    author-avatar
    whdibk30
    这个家伙很懒,什么也没留下!
    PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
    Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有