解决gorm中文表名或表头报错和乱码问题,gorm中文报错如何解决?

报错

2021/11/20 16:39:51 C:/Users/asus/3D Objects/个人项目/go网络编程测试/GormTest/主.go:45 Error 1300: Invalid utf8 character string: ‘a\xE6\xB5\x8B\xEF\xBF\xBD\xEF\xBF\xBD\x95’
[5.002ms] [rows:0] CREATE TABLE a测��� (id bigint unsigned AUTO_INCREMENT,created_at datetime NULL,updated_at datetime NULL,deleted_at datetime NULL,uid bigint unsigned,a数��� varchar(256),PRIMARY KEY (id),INDEX idx_a测���_deleted_at (deleted_at))ENGINE=InnoDB DEFAULT CHARSET=utf8mb4

2021/11/20 16:39:51 C:/Users/asus/3D Objects/个人项目/go网络编程测试/GormTest/主.go:48 Error 1300: Invalid utf8 character string: ‘a\xE6\xB5\x8B\xEF\xBF\xBD\xEF\xBF\xBD\x95’
[14.143ms] [rows:0] INSERT INTO a测��� (created_at,updated_at,deleted_at,uid,a数���) VALUES (‘2021-11-20 16:39:51.916’,‘2021-11-20 16:39:51.916’,NULL,100,‘寻觅’)
在这里插入图片描述
只是单纯的阻止报错的话,可以把DSN中的charset=utf8换成charset=utf8mb4即可,但这还是会乱码解决了但没完全解决。

解决方案

  1. 使用数字或者字母结束。
package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"log"
)

type A测试a struct {
	gorm.Model
	Uid uint
	A数据a string
}


func main() {
	db, err := gorm.Open(mysql.New(mysql.Config{
		// DSN data source name 数据源名称
		DSN: "用户名:密码@tcp(ip:端口)/数据库?charset=utf8mb4&parseTime=True&loc=Local",
		// string 类型字段的默认长度
		DefaultStringSize: 256,
	}), &gorm.Config{})
	if err != nil {
		log.Panicf("-------------------数据库连接失败!-------------------\n++++++++%v++++++++", err)
	}
	A测试1 := A测试a{A数据a: "寻觅", Uid: 100}
	// 自动迁移 架构(schema)
	db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4").AutoMigrate(&A测试a{})

	// 创建
	db.Create(&A测试1)
}

不过这样配置出来的表名和表头看上会比较难看,如果不介意的话可以使用这种方法。
在这里插入图片描述
2. 关闭大写自动转小写功能NoLowerCase: true,

package main

import (
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"log"
)

type A测试 struct {
	gorm.Model
	Uid uint
	A数据 string
}


func main() {
	db, err := gorm.Open(mysql.New(mysql.Config{
		// DSN data source name 数据源名称
		DSN: "寻觅:weh*gxu*RXW8ujp3cur@tcp(192.168.1.205:3306)/Go测试?charset=utf8mb4&parseTime=True&loc=Local",
		// string 类型字段的默认长度
		DefaultStringSize: 256,
		SkipInitializeWithVersion: false,
	}), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{
			// 这里可以加个前缀
			//TablePrefix: "寻觅_",
			// 使用单数表名,就是不让表名自动加s,看起来比较舒服
			SingularTable: true, 
			// 关闭大写转小写,这是关键,启动这个就不在会导致中文乱码!
			NoLowerCase: true,
		},
	})
	if err != nil {
		log.Panicf("-------------------数据库连接失败!-------------------\n++++++++%v++++++++", err)
	}
	A测试1 := A测试{A数据: "寻觅", Uid: 100}
	// 自动迁移 架构(schema)
	db.Set("gorm:table_options", "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4").AutoMigrate(&A测试{})
	// 创建
	db.Create(&A测试1)
}

在这里插入图片描述

原因分析

经过对gorm的代码审计和debug测试,导致这个乱码的是下列代码,在遍历的时候直接生硬的少取了一位,但因为中文的编码在这里是占用了三个字节的,这样生硬的减去一个字节位就导致了乱码,然后进一步引发了报错。

	var (
		value                          = commonInitialismsReplacer.Replace(name)
		buf                            strings.Builder
		lastCase, nextCase, nextNumber bool // upper case == true
		curCase                        = value[0] <= 'Z' && value[0] >= 'A'
	)
	
	// 就是这里的[:len(value)-1]导致的报错
	// go直接遍历中文会将中文转换成`UTF-16BE`编码的十进制用int32表达
	for i, v := range value[:len(value)-1] {
		nextCase = value[i+1] <= 'Z' && value[i+1] >= 'A'
		nextNumber = value[i+1] >= '0' && value[i+1] <= '9'

		if curCase {
			if lastCase && (nextCase || nextNumber) {
				buf.WriteRune(v + 32)
			} else {
				if i > 0 && value[i-1] != '_' && value[i+1] != '_' {
					buf.WriteByte('_')
				}
				buf.WriteRune(v + 32)
			}
		} else {
			buf.WriteRune(v)
		}

		lastCase = curCase
		curCase = nextCase
	}

	if curCase {
		if !lastCase && len(value) > 1 {
			buf.WriteByte('_')
		}
		buf.WriteByte(value[len(value)-1] + 32)
	} else {
		buf.WriteByte(value[len(value)-1])
	}
	ret := buf.String()
	return ret

这里我们把这块导致乱码的代码单独拿出来看,前两个字符输出的都是没有问题的,a占用一个字节,占用三个字节(这里的占用是utf-8),正常情况下也应该占用三个字节,但是因为这里少取一个,导致他成了两个字符

package main

import "fmt"

func main() {
	value := "a寻觅"
	for i, v := range value[:len(value)-1] {
		fmt.Println("--------", i, v)
	}
}

在这里插入图片描述
这两个字符是无法组成一个正常字的,所以这里输出变成了65533,Unicode一般无法理解的字符才会用这个来表示。
在这里插入图片描述
因为这是大小写转换对最后一位处理不当导致的,所以我们直接关掉大小写转换功能或者把最后一位改成ascii编码即可解决。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

寻_觅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值