李迟2022年10月工作生活总结(代码片段)

李迟 李迟     2022-11-29     777

关键词:

本文为 2022 年 10 月工作生活总结。

研发编码

Go

判断interface是否为空

判断interface类型是否为空,不能直接使用==nil来判断(用之无效),需先用reflect.ValueOf判断为指针类型,再使用IsNil函数判断,如下:

func IsInterfaceNil(i interface) bool 
	vi := reflect.ValueOf(i)
	if vi.Kind() == reflect.Ptr 
		return vi.IsNil()
	
	return false

结构体字段对比

最近进行的一个工程,在集成测试中需要对比请求的结果和已知的结果,里面有大量的较多字段的结构体,幸好之前实现了结构体字段对比的接口,但不够完善,为了泛化,参数使用了interface,再用反射获取字段及类型,而对比时,则要一一判断结构体字段类型,因为不知道有没有更优雅的方法实现,每种类型都要用一个case判断,不太简洁,所以偷懒把int8uint8等几个用到的做判断,这次把常见的基础类型都加了,应该够用了。

func CompareStruct(data, data1 interface, point int, skipname ...string) (info string) 
	ret := false
	info = ""
	// 预防为空
	if IsInterfaceNil(data) || IsInterfaceNil(data1) 
		return
	

	var skipNames []string
	for _, item := range skipname 
		skipNames = append(skipNames, item)
	

	check := func(a string, b []string) bool 
		for _, item := range b 
			if strings.EqualFold(item, a) 
				return true
			
		
		return false
	

	object := reflect.ValueOf(data)
	myref := object.Elem()
	// fmt.Printf("myref: %v %v\\n", data, object)
	//typeOfType := myref.Type()
	for i := 0; i < myref.NumField(); i++ 
		// 如果不需要对比,跳过
		if ok := check(myref.Type().Field(i).Name, skipNames); ok 
			continue
		

		// 浮点数精度
		mypoint := 4
		if point != 0 
			mypoint = point
		
		field := myref.Field(i)
		field1 := reflect.ValueOf(data1).Elem().Field(i)
		switch field.Type().Name() 
		case "int8":
			ret = field.Interface().(int8) == field1.Interface().(int8)
		case "uint8":
			ret = field.Interface().(uint8) == field1.Interface().(uint8)
		case "int16":
			ret = field.Interface().(int16) == field1.Interface().(int16)
		case "uint16":
			ret = field.Interface().(uint16) == field1.Interface().(uint16)
		case "int":
			ret = field.Interface().(int) == field1.Interface().(int)
		case "uint":
			ret = field.Interface().(uint) == field1.Interface().(uint)
		case "int32":
			ret = field.Interface().(int32) == field1.Interface().(int32)
		case "uint32":
			ret = field.Interface().(uint32) == field1.Interface().(uint32)
		case "int64":
			ret = field.Interface().(int64) == field1.Interface().(int64)
		case "uint64":
			ret = field.Interface().(uint64) == field1.Interface().(uint64)
		case "string":
			ret = field.Interface().(string) == field1.Interface().(string)
		case "float64":
			ret = IsFloatEqual(field.Interface().(float64), field1.Interface().(float64), mypoint)
		case "float32":
			ret = IsFloatEqual(float64(field.Interface().(float32)), float64(field1.Interface().(float32)), mypoint)
		case "bool":
			ret = field.Interface().(bool) == field1.Interface().(bool)
		default:
			ret = false
		
		if ret == false 
			info1 := fmt.Sprintf("%s: %v != %v ", myref.Type().Field(i).Name, field.Interface(), field1.Interface())
			info = info + info1
		
	
	return info


测试示例如下:

func TestStruct(t *testing.T) 
	object1 := TestObj
		Name:  "James",
		Value: 128,
		Size:  256,
		Guard: 56.4,
	
	object2 := TestObj
		Name:  "James1",
		Value: 128,
		Size:  259,
		Guard: 56.4,
	

	info := CompareStruct(&object1, &object2, 0, "Name")
	fmt.Println("compare: ", info)



为了不把问题复杂化,该函数不支持结构体嵌套,有此种情形的,需要过滤掉,再单独调用。

由上述接口启发,有些场合要检查结构体的字符串字段是否为空,因为结构体还可能有其它类型字段,所以要指定检查的字段名称,实现如下:

func CheckStructEmpty(data interface, checkname ...string) (ret int) 
	ret = 0

	// 预防为空
	if IsInterfaceNil(data) 
		ret = 1
		return
	

	var checkNames []string
	for _, item := range checkname 
		checkNames = append(checkNames, item)
	

	check := func(a string, b []string) bool 
		for _, item := range b 
			if strings.EqualFold(item, a) 
				return true
			
		
		return false
	

	object := reflect.ValueOf(data)
	myref := object.Elem()
	// fmt.Printf("myref: %v %v\\n", data, object)
	//typeOfType := myref.Type()
	for i := 0; i < myref.NumField(); i++ 
		// 如果不需要对比,跳过
		if ok := check(myref.Type().Field(i).Name, checkNames); !ok 
			continue
		

		field := myref.Field(i)
		switch field.Type().Name() 
		case "string":
			ret1 := field.Interface().(string) == ""
			if ret1 
				ret = 1
			
		default:
			ret = 0
		
	
	return

函数返回值为1表示为空,0表示非空。由于不支持嵌套结构体形式,所以复杂结构体需要调用多次函数,将返回值累加,如果不为0,则说明有一字段为空。上述代码仅判断string类型,可类推到其它类型,也可包含检查范围,但就变得复杂了。

go 版本导致的编译出错

# golang.org/x/exp/constraints
vendor/golang.org/x/exp/constraints/constraints.go:13:2: invalid character U+007E '~'
vendor/golang.org/x/exp/constraints/constraints.go:13:7: syntax error: unexpected |, expecting semicolon or newline or 
vendor/golang.org/x/exp/constraints/constraints.go:20:2: invalid character U+007E '~'
vendor/golang.org/x/exp/constraints/constraints.go:20:8: syntax error: unexpected |, expecting semicolon or newline or 

错误较奇怪,到 github 仓库上查看源码,到能编译通过的机器查源码,都和出错的代码一样。后来才发现,出错所用的 golang 版本是 1.16,换成 1.18 即解决问题。

工作记录

这个月做了很多不属于自己而又花费时间的事,但又不得不做。因为自己的事务要测试,所以在服务器部署了 oracle 数据库,而后顺便部署了 mysql,再顺便帮忙创建各种数据表,再到帮忙开放端口,创建用户,最终负责维护服务器。一开始就是老好人的人设,不知怎么能改善这种状态。

这个月进行了一次 docker 入门的培训。由于是当天早上领导要求当天下午培训,于是用 Markdown 写了个文档,幸好以前有积累,到网上找了些图示,再整理下文字进行补充,然后导出pdf,就成了培训资料。不过,由于时间紧,而又新引入容器,在开发阶段,不是所有人都会接受,而事情只要有人做就行,所以还是由我代劳,但自身又有编码任务,不能占用过多的上班时间。在镜像中适配服务,涉及各种依赖库,很是麻烦,但得做。

月中公司挂牌了,阵仗比较大。但该怎么工作还是怎么工作,不知道后面会不会有新的饼。因为依旧25号发上个月工资,所以目前及未来一段时间应该没有变化(至少从这点能猜出一二)。

生活记录

不知何时起,才2岁半的大妞竟然能生气几个小时。小孩比较单纯,脑子想着什么,马上表现出来。正常情况下,大妞到处跑,逗大人,玩玩具,找零食吃。如果有什么事不顺她意,或大人语气重些,就会到墙角站着,或去阳台站着。如果是大锤,则是哭。最终结果都是不理人,不说话,怎么安慰也无济于事。不知他们长大一点,兄妹吵架,又会是怎样的情形。

某周日晚上睡前,大妞唱了句“世上只有爸爸好”,被妈妈教育,大妞生气,于是她妈妈加大教育力度,顺便从大锤出生到当前的不好的事情细数了一次,根据经验,我是不能也不敢插话的。于是小孩又是半夜才入睡,而我也因为这个事耽搁,又熬到2点多才基本完成工作,算是能暂时交差。
我知道小孩是有“秩序敏感期”,但不知道小孩某些无知的“创造”是否有理论支撑。人与人的生活经历不同,对某些事物的感受不同,因而不可避免会出现观点的差异,所以才要“求同存异”,只是不是所有的人都有这个想法,比如,大锤接到他妈妈给的东西,一定要说“谢谢”,而我是无所谓的,我甚至刻意让大锤记住家里所有的事都是他妈妈做的,他爸爸只会玩手机和玩游戏。
幸好在公司里面,部门研发氛围较好,至于我没感受到尔虞我诈,相互推诿(当然要抛开外包和正式员工的差别这个事实)。只是家里就一地鸡毛了。

月底前几天,看到小区大门门禁被折,一些健身器材被毁,垃圾满地,才知道是物业要走了,新的物业又没来。后来结合小区公告、法院文书、网上新闻,才知道一些事由。小区建成之初,开发商旗下的物业入住手续不合法,至今有十多年了,几年前开始官司,物业成了被告;上半年,业委会成了被执行人。
现在,物业被限期撤走,从公告上看,临走前要把属于他们的资产搬走,并保留追缴物业费的权利。后来小区挂起横幅,既有欢迎新物业的,又有新物业承诺的。后来新物业贴出公开信,表明了要稳定、要提升的态度和决心,同时提到由于旧物业没有留下资料,要重新登记云云。物业和业主本来就是对立的,虽说有业委会代表业主,但其只是代表而已。月底最后一天,看到广西较大的报社报道到这事,看来还是挺大的,不过还是看开点罢。 话说,工作以来,也换了很多家公司,但从不敢删库跑路,也和原同事保持着关系,因为南宁搞IT的圈子太小了,做人留一线,日后好相见。

思想方面

关于程序默认行为的一点思考。

之前实现的数据校验(后因事务中断,转由其它同事继续开发),需要设计校验的配置文件,一开始设计时,在每个数据表字段都加上need参数,值为1表示要校验,为0表示不要校验。后发觉此法繁琐,因为做这个 工作的程序本身就是校验,没必要画蛇添足人为加上限制,如果某字段不需校验,配置文件不出现此字段即可。即:配置文件中所列出的,都是要校验,无必要不列出。

golangswitch默认会退出case条件,如要继续执行,则显式加上fallthrough。而C/C++语言,则是相反,如退出case条件,需显式加break。两种语言行为表现截然不同,只能编码时留意,没有很好的理解方法。

无常

使用了十余年的域名明年到期,就不再使用了。以前注册了若干个云盘,也无法再使用了(现在维护着价格不菲的云主机,保存和共享资料)。2008年注册使用至今的CSDN,也有很大变化,我上传的0积分资源变成要积分了,有很多资源收费了,我拥有的1千多C币也没了。唯一不变的是变化本身,只能拥着变化,跟上时代步伐。不过,我还是比较怀旧,听歌要听二十年前的,看电影亦然。像我维护着的 golang 工程,也依然使用着 KubeEdge 的模块注册框架。

业余研究

这个月除了写代码外,没干啥事,而且代码还是业务代码,没有做研究提升技能。思考再三,决定拿些古文来背。反正一天通勤有2个小时,时间是有的,家庭工作两不误。一来练练脑子防止老化,二来教大锤背。当然,对此事既不炫耀沾沾自喜,也不强求背多少。当登高远眺时,能想到“山原旷其盈视”、想到“天高地迥,觉宇宙之无穷”,想到“仰观宇宙之大,俯察品类之盛”,感受一下古人的情怀。当意不平,遇到坎时,能用些话抚平内心,激励自己。这就够了。
后来罗师傅说他买了套李白集校注,我不甘人后,作为价格敏感型消费者,趁着提前开始的双十一,买了史记、三国志,还有古文观止等书装门面。后来又和罗师傅相约去古玩市场淘些古籍。当然这些书并不会专门抽时间阅读,可能兴之所至,偶尔翻翻。毕竟这玩意不能当饭吃,写代码才有饭吃。

李迟2022年12月工作生活总结(代码片段)

本文为2022年12月工作生活总结。研发编码Go一个Golang的编译脚本mybuild.sh:#!/bin/bashVersion="v0.0.1"VER=""if[$#=1];thenVER=$1Version=""$VERelseecho"notsetversion,using 查看详情

李迟2022年12月工作生活总结(代码片段)

本文为2022年12月工作生活总结。研发编码Go一个Golang的编译脚本mybuild.sh:#!/bin/bashVersion="v0.0.1"VER=""if[$#=1];thenVER=$1Version=""$VERelseecho"notsetversion,using 查看详情

李迟2022年11月工作生活总结(代码片段)

本文为2022年11月工作生活总结。研发编码Go研究了一些结构体、json相关的东西,感觉挺方便的,有空单独写出来。Docker使用alphine构建的一个应用服务镜像,其中有一解压zip文件功能sh./config/extract.sh./data/website/Archive:/tm... 查看详情

李迟2022年11月工作生活总结(代码片段)

本文为2022年11月工作生活总结。研发编码Go研究了一些结构体、json相关的东西,感觉挺方便的,有空单独写出来。Docker使用alphine构建的一个应用服务镜像,其中有一解压zip文件功能sh./config/extract.sh./data/website/Archive:/tm... 查看详情

李迟2022年6月工作生活总结(代码片段)

本文为2022年6月工作生活总结。研发编码前端需求:某输入框自动补全,带有中文名称,但后端只处理数字,需解析出数字并序列化。如将梧州岑溪001改为001,但页面依然显示前者。解析函数如下:functionde... 查看详情

李迟2022年6月工作生活总结(代码片段)

本文为2022年6月工作生活总结。研发编码前端需求:某输入框自动补全,带有中文名称,但后端只处理数字,需解析出数字并序列化。如将梧州岑溪001改为001,但页面依然显示前者。解析函数如下:functionde... 查看详情

李迟2022年6月工作生活总结(代码片段)

本文为2022年6月工作生活总结。研发编码前端需求:某输入框自动补全,带有中文名称,但后端只处理数字,需解析出数字并序列化。如将梧州岑溪001改为001,但页面依然显示前者。解析函数如下:functionde... 查看详情

李迟2022年9月工作生活总结(代码片段)

本文为2022年9月工作生活总结。研发编码Java调试时发现在Java函数体代码中途使用return返回,其后的代码会报错,提示:java:无法访问的语句不知何故。只好用if(1==0)...这样的形式来达到目的。Go协程及同步相关Go... 查看详情

李迟2022年9月工作生活总结(代码片段)

本文为2022年9月工作生活总结。研发编码Java调试时发现在Java函数体代码中途使用return返回,其后的代码会报错,提示:java:无法访问的语句不知何故。只好用if(1==0)...这样的形式来达到目的。Go协程及同步相关Go... 查看详情

李迟2022年9月工作生活总结(代码片段)

本文为2022年9月工作生活总结。研发编码Java调试时发现在Java函数体代码中途使用return返回,其后的代码会报错,提示:java:无法访问的语句不知何故。只好用if(1==0)...这样的形式来达到目的。Go协程及同步相关Go... 查看详情

李迟2022年8月工作生活总结(代码片段)

本文为2022年8月工作生活总结。研发编码Java接手了一个Java工程事务(注:不是接手工程),在其中添加一个模块。不知为何领导会找到我,或者是没有负责项目的原故,作为十分珍惜工作的人,不敢拒... 查看详情

李迟2022年11月工作生活总结(代码片段)

本文为2022年11月工作生活总结。研发编码Go研究了一些结构体、json相关的东西,感觉挺方便的,有空单独写出来。Docker使用alphine构建的一个应用服务镜像,其中有一解压zip文件功能sh./config/extract.sh./data/website/Archive:/tm... 查看详情

李迟2022年12月工作生活总结(代码片段)

本文为2022年12月工作生活总结。研发编码Go一个Golang的编译脚本mybuild.sh:#!/bin/bashVersion="v0.0.1"VER=""if[$#=1];thenVER=$1Version=""$VERelseecho"notsetversion,using"$Versionecho"plsconfirm(pressenter)"readfiec... 查看详情

李迟2023年01月工作生活总结(代码片段)

本文为2023年1月工作生活总结。研发编码Linux测试机器端口连通性有多种方法。下面演示的,前者是不开放端口,后者是开放的。ssh命令$ssh-v-p9001localhostdebug1:connecttoaddress127.0.0.1port9001:Connectionrefusedssh:connecttohostlocalhostport900... 查看详情

李迟2022年4月工作生活总结

本文为2022年4月工作生活总结。研发编码Linux无其它思考业主要求做的事则一定要做,这是态度问题,但需要把风险点提前说清楚。工作记录无生活记录下旬开始,大锤发烧咳嗽持续了5天左右,先是发烧2天,... 查看详情

李迟2022年3月工作生活总结

本文为2022年3月工作生活总结。研发编码Linux研究了应用程序和动态库环境变量传递问题。编码其它无前后端在golang中渲染html页面,将部分信息处理后再显示到页面,比如系统信息和系统运行时长。在ajax中直接用串行化... 查看详情

李迟2022年5月工作生活总结(代码片段)

本文为2022年5月工作生活总结。研发编码Linux查看gcc支持的默认宏定义(以不同平台的linux系统为例):$gcc-E-dM-</dev/null重要的:#define__unix__1#defineunix1#define__unix1#define__linux1#define__linux__1#definelinux1#define__INT32_MAX__214... 查看详情

李迟2023年01月工作生活总结(代码片段)

本文为2023年1月工作生活总结。研发编码Linux测试机器端口连通性有多种方法。下面演示的,前者是不开放端口,后者是开放的。ssh命令$ssh-v-p9001localhostdebug1:connecttoaddress127.0.0.1port9001:Connectionrefusedssh:connecttohostlocalhostport900... 查看详情