Golang不支持UTF8编码的原因分析
Golang不支持UTF8编码的原因分析 大家好,
我一直在绞尽脑汁地尝试让我的 Golang API 通过 gorm 解析 MySQL 数据库的列,但在处理包含外文字符的条目时却毫无进展。网上似乎没有任何关于此的指南,所以我希望能在这里得到一些帮助。
我有一个包含许多含有日文字符的表的数据库。例如,某一行中有这个字符串:
美味しい
然而,当在数据库中对此包含该文本值的条目执行 SELECT 查询时,它总是返回:
美味ã—ã„
我已经确保数据库是正确使用 utf8mb4 创建的,甚至将此特定列在表上设置为 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci。对于我的 gorm.Open 调用,我在末尾设置了以下参数以确保它解析 utf8mb4:?charset=utf8mb4&parseTime=true
当数据库已正确设置时,我该如何让 Golang 正确支持 UTF8?
如果有帮助的话,以下是数据库中预期的 UTF8 数据:
mysql> select alt_text from picture_details where id=136;
+--------------+
| alt_text |
+--------------+
| 美味しい |
+--------------+
1 row in set (0.00 sec)
是的,我也尝试过将文本手动编码为 \xe7\xbe\x8e\xe5\x91\xb3\xe3\x81\x97\xe3\x81\x84 字符串,看看是否能解决问题(结果并没有)。
谢谢!
更多关于Golang不支持UTF8编码的原因分析的实战教程也可以访问 https://www.itying.com/category-94-b0.html
哇,我明白了……
我深入查看了我的数据库代码,发现数据库本身由于某些原因没有设置为utf8mb4。我显然漏掉了整个事情中最重要的一环。 由于这是一个测试数据库,我不得不重新创建它:
CREATE DATABASE IF NOT EXISTS my_database CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
当然,我现有的数据库必须用以下命令修改:
ALTER DATABASE my_database CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
但这还不是全部。我还必须将以下内容添加到我的docker-compose命令中:
--skip-character-set-client-handshake
我曾经一度只设置了其中一项,但没有像刚才那样同时设置这两项。显然两者都是必需的。
感谢您的帮助,并指出了它变成字节数组这个有趣的事实。我的API现在终于能返回日语了!
更多关于Golang不支持UTF8编码的原因分析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
k71:
当数据库正确设置时,如何让 Golang 正确支持 UTF8?
这是 Go 编程语言:Go 编程语言规范。
Rob 是 Go 和 UTF-8 的共同作者。因此,Go 正确地支持 UTF-8。
你没有提供一个小的、可复现的代码示例,所以我们不知道你的问题是什么。
然而,你似乎将字符解释为字节切片而不是字符串。
package main
import (
"fmt"
)
func main() {
jp := `美味しい` // UTF-8 as string
fmt.Println(jp)
bs := []byte(jp) // UTF-8 as byte slice
fmt.Printf("%c\n", bs)
s := string(bs) // UTF-8 as string
fmt.Println(s)
}
美味しい
[ç ¾ å ³ ã ã ]
美味しい
你好 petrus,
感谢你的回复。了解到它应该支持UTF8编码,这无疑是个好消息。由于数据是从数据库提取并通过 fmt.Println 直接打印的,除了GORM操作和模型定义外,我没有太多代码可以分享,但我会在下面提供我所拥有的内容。
正如你指出的,它是一个 []byte 数组,这很有趣。然而,字节数组中似乎出现了一些额外的字符或其他东西。我基本上以你展示的代码片段为例:
s2 := string(pictures[0].AltText)
fmt.Println(s2)
jp := `美味しい` // UTF-8 字符串
fmt.Println(jp)
bs := []byte(jp) // UTF-8 字节切片
fmt.Printf("%c\n", bs)
s := string(bs) // UTF-8 字符串
fmt.Println(s)
输出:
美味ã—ã„
美味しい
[ç ¾ å ³ ã ã
]
美味しい
我决定找出这些字节的十六进制/二进制表示,看看是否有相似之处。对于来自数据库的条目和硬编码的字符串,我只是打印出它们的值并进行比较,但除了它们长度相同之外,没有发现任何真正相似的地方:
pictures[0].AltText: c3 c2 c5 c3 e2 c2 c3 c2 e2 c3 c2 e2
bs: e7 be 8e e5 91 b3 e3 81 97 e3 81 84
基本上,以下是我关于模型和正在运行的GORM命令的内容(我已将其简化为仅关注 alt_text,忽略了模型和扫描的其他细节):
type PictureDetails struct {
AltText string `json:"altText"`
Language string `json:"language"`
...
}
type Picture struct {
AltText string `gorm:"<-:false" json:"altText,omitempty"`
Language string `gorm:"<-:false" json:"language,omitempty"`
PictureDetails []PictureDetails `gorm:"-" json:"pictureDetails,omitempty"`
...
}
var pictures []models.Picture
db.Table("pictures").
Select("pictures.*, picture_details.alt_text, picture_details.picture_language").
Joins("left join picture_details on picture_details.picture_id = pictures.id").
Where("picture_language = ?", language).
Scan(&pictures)
API为给定语言返回的数组如下:
[
{
altText: "<Japanese here>"
...
},
...
]
Go语言原生支持UTF-8编码,你遇到的问题实际上是数据库连接字符集配置问题。以下是解决方案:
package main
import (
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
func main() {
// 正确的DSN格式
dsn := "user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic("failed to connect database")
}
// 验证连接字符集
var result string
db.Raw("SHOW VARIABLES LIKE 'character_set_connection'").Scan(&result)
fmt.Printf("Connection charset: %s\n", result) // 应该显示utf8mb4
// 查询示例
type PictureDetail struct {
ID uint
AltText string
}
var detail PictureDetail
db.Where("id = ?", 136).First(&detail)
fmt.Printf("AltText: %s\n", detail.AltText) // 应该正确显示"美味しい"
}
如果问题仍然存在,检查数据库连接的collation设置:
// 设置会话级别的字符集
db.Exec("SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci")
// 或者使用连接后配置
sqlDB, err := db.DB()
if err != nil {
panic(err)
}
defer sqlDB.Close()
// 设置连接参数
sqlDB.SetConnMaxLifetime(time.Minute * 3)
sqlDB.SetMaxOpenConns(10)
sqlDB.SetMaxIdleConns(10)
// 验证当前连接的字符集
var charset, collation string
row := db.Raw("SELECT @@character_set_connection, @@collation_connection").Row()
row.Scan(&charset, &collation)
fmt.Printf("Charset: %s, Collation: %s\n", charset, collation)
确保你的MySQL版本支持utf8mb4(5.5.3+),并且表的字符集确实正确设置:
// 检查表字符集
type TableCharset struct {
Table string
Charset string
Collation string
}
var tableInfo TableCharset
db.Raw(`
SELECT
TABLE_NAME as Table,
CHARACTER_SET_NAME as Charset,
COLLATION_NAME as Collation
FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = 'picture_details'
AND COLUMN_NAME = 'alt_text'
`).Scan(&tableInfo)
fmt.Printf("Table: %s, Charset: %s, Collation: %s\n",
tableInfo.Table, tableInfo.Charset, tableInfo.Collation)
如果以上配置都正确,问题可能出现在数据插入阶段。确保插入时也使用正确的字符集:
// 插入数据示例
newDetail := PictureDetail{
AltText: "美味しい",
}
db.Create(&newDetail)
Go语言的字符串内部使用UTF-8编码,只要数据库连接配置正确,就能正确处理多语言字符。


