Open dotqi opened 1 year ago
所以应该改动哪里呢
我个人的处理办法是在param.go的67行下增加
if p.isDescribed && (p.SQLType == api.SQL_VARCHAR || p.SQLType == api.SQL_CHAR) {
size = api.SQLULEN(len(d))
if size < 1 {
// size cannot be less then 1 even for empty fields
size = 1
}
}
个人能力有限。你要好好测试一下。
@dotqi 好奇请教一下
UTF16 和 UTF8 在中文长度上有区别么?
package main
import ( "fmt" "unicode/utf16" )
func main() { val := "你好" fmt.Println(val) u16 := StringToUTF16(val) fmt.Println("u16", len(u16), u16) // u16 3 [20320 22909 0] u8 := []byte(val) fmt.Println("u8", len(u8), u8) // u8 6 [228 189 160 229 165 189] }
func StringToUTF16(s string) []uint16 { return utf16.Encode([]rune(s + "\x00")) }
package main
import ( "fmt" "unicode/utf16" )
func main() { val := "你好" fmt.Println(val) u16 := StringToUTF16(val) fmt.Println("u16", len(u16), u16) // u16 3 [20320 22909 0] u8 := []byte(val) fmt.Println("u8", len(u8), u8) // u8 6 [228 189 160 229 165 189] }
func StringToUTF16(s string) []uint16 { return utf16.Encode([]rune(s + "\x00")) }
@dotqi 你这个实验,我做了,感觉你这个不对
u16 := StringToUTF16(val)
fmt.Println("u16", len(u16), u16)
u8 := []byte(val)
fmt.Println("u8", len(u8), u8)
但实际上,u16 可以转回成 []rune,他们的长度是一模一样的
val := "你好𠀾" []uint16 = [20320 22909 55360 56382] []rune = [20320 22909 131134] []byte = [228 189 160 229 165 189 240 160 128 190]
rune 实际是 int32,比 uint16 表示的多,131134 和 55360 56382 都表示 “𠀾”,它实际占 4 个字节 [240 160 128 190]
@dotqi
分析:
b := api.StringToUTF16(d)
p.Data = b
buf = unsafe.Pointer(&b[0])
l := len(b)
l -= 1 // remove terminating 0
size = api.SQLULEN(l)
if size < 1 {
// size cannot be less then 1 even for empty fields
size = 1
}
l *= 2 // every char takes 2 bytes
buflen = api.SQLLEN(l)
plen = p.StoreStrLen_or_IndPtr(buflen)
错误的点:
l *= 2 // every char takes 2 bytes
4字节的 utf16 转换后是 2 个 uint16,2 没问题
但是 3字节的 utf16 转换后可能是 1 个 uint16,这里 2 就错了,*3才对
按照 case []byte:
的改写:
case string:
ctype = api.SQL_C_WCHAR
// b := api.StringToUTF16(d)
// p.Data = b
// buf = unsafe.Pointer(&b[0])
// l := len(b)
// l -= 1 // remove terminating 0
// size = api.SQLULEN(l)
// if size < 1 {
// // size cannot be less then 1 even for empty fields
// size = 1
// }
// l *= 2 // every char takes 2 bytes
// buflen = api.SQLLEN(l)
// plen = p.StoreStrLen_or_IndPtr(buflen)
b := []byte(d)
p.Data = append(b, byte(0))
buf = unsafe.Pointer(&b[0])
buflen = api.SQLLEN(len(b))
plen = p.StoreStrLen_or_IndPtr(buflen)
size = api.SQLULEN(len(b))
if size < 1 {
// size cannot be less then 1 even for empty fields
size = 1
}
大胆点:因为使用了 append 所以不用再判断 size < 1 了,我不太明白为什么 buflen 不能包含 \0 的长度,所以参照原来减 1 了
b := append([]byte(d), byte(0)) p.Data = b buf = unsafe.Pointer(&b[0]) buflen = api.SQLLEN(len(b) - 1) plen = p.StoreStrLen_or_IndPtr(buflen) size = api.SQLULEN(len(b))
谢谢,回头按你的方式测试一下。
向 varchar字段保存中文中会被截断。 测试发现。文件param.go绑定strinig时。size取的是转为UTF16后的长度。 但一个中文在UTF8时占用3个字节。size应该取UTF8的长度。 这样才可以保证向varchar字段保存中文时不会被截断。