Go语言中for循环模拟while使用时未正常退出的问题
问题:Go语言中UTF-8转Latin-1循环未按预期退出
我需要将UTF-8字符串转换为Latin-1兼容字符串(把所有Latin-1不支持的字符替换为'?'),以适配仅支持Latin-1的系统并保存到txt文件中。为此编写了测试代码:
package main import ( "errors" "fmt" "os" "golang.org/x/text/encoding/charmap" "golang.org/x/text/transform" ) func main() { strUTF8 := "example 1: Г, example 2: ≤, example 3: “, etc" // utf-8字符串,包含3个不兼容Latin-1的字符 t := charmap.ISO8859_1.NewEncoder() // Latin-1编码器 ini := 0 // 字符串处理起始位置 strISO88591 := "" // 转换后的Latin-1兼容字符串 counter := 0 // 计数器,强制循环执行5次后中断(原循环逻辑未按预期工作) err := errors.New("initiate err with non-nil value") // 初始化非nil错误以进入循环 for err != nil { // 预期err为nil时退出循环 str, n, err := transform.String(t, strUTF8[ini:]) if err != nil { ini = ini + n runes := []rune(strUTF8[ini:]) ini = ini + len(string(runes[0])) // 跳过不兼容的字符,更新下一次处理的起始位置 str = str + "?" } strISO88591 = strISO88591 + str // 打印中间结果 fmt.Println("sISO88591:", strISO88591) fmt.Println("err:", err) fmt.Println("err!=nil:", err != nil) fmt.Println() // 以下注释的代码可以让循环正常退出,但为什么for条件判断不生效? //if err == nil { // break //} // 计数器强制中断循环 counter += 1 if counter > 4 { fmt.Println("breaking forcibly") break } } f, _ := os.Create("test.txt") defer f.Close() _, err = f.WriteString(strISO88591) if err != nil { panic(err) } }
终端输出如下:
sISO88591: example 1: ? err: encoding: rune not supported by encoding. err!=nil: true sISO88591: example 1: ?, example 2: ? err: encoding: rune not supported by encoding. err!=nil: true sISO88591: example 1: ?, example 2: ?, example 3: ? err: encoding: rune not supported by encoding. err!=nil: true sISO88591: example 1: ?, example 2: ?, example 3: ?, etc err: <nil> err!=nil: false sISO88591: example 1: ?, example 2: ?, example 3: ?, etc, etc err: <nil> err!=nil: false breaking forcibly
使用Go版本为go1.19.5 windows/amd64,代码中用for循环模拟while逻辑,设置循环条件为err != nil,预期err为nil时退出循环,但实际循环并未退出,直到通过计数器强制中断,请问哪里操作有误?
问题分析与解决
错误原因
循环条件判断的err和循环内更新的err不是同一个变量。在循环内执行str, n, err := transform.String(...)时,:=是短变量声明,会在循环作用域内创建一个新的局部err变量,覆盖了外部的err变量。因此for循环的条件判断始终读取的是外部初始化的err值(初始为非nil),不会感知到循环内局部err变为nil的变化,导致循环无法自动退出。
修正方案
把循环内的短变量声明改为赋值操作,确保循环内更新的是外部的err变量。需要提前在循环外声明循环内用到的str和n变量,或者在循环内先声明再赋值。
修正后的代码:
package main import ( "errors" "fmt" "os" "golang.org/x/text/encoding/charmap" "golang.org/x/text/transform" ) func main() { strUTF8 := "example 1: Г, example 2: ≤, example 3: “, etc" // utf-8字符串,包含3个不兼容Latin-1的字符 t := charmap.ISO8859_1.NewEncoder() // Latin-1编码器 ini := 0 // 字符串处理起始位置 strISO88591 := "" // 转换后的Latin-1兼容字符串 var err error = errors.New("initiate err with non-nil value") // 初始化非nil错误以进入循环 for err != nil { // 当err为nil时自动退出循环 var str string var n int // 使用赋值操作,更新外部的err变量 str, n, err = transform.String(t, strUTF8[ini:]) if err != nil { ini += n runes := []rune(strUTF8[ini:]) ini += len(string(runes[0])) // 跳过不兼容的字符 str += "?" } strISO88591 += str // 打印中间结果 fmt.Println("sISO88591:", strISO88591) fmt.Println("err:", err) fmt.Println("err!=nil:", err != nil) fmt.Println() } f, _ := os.Create("test.txt") defer f.Close() _, err = f.WriteString(strISO88591) if err != nil { panic(err) } }
说明
修正后,循环内的err会直接更新外部变量,for循环的条件判断能正确读取到err的最新值,当转换完成err变为nil时,循环会自动退出,无需再用计数器强制中断。
内容的提问来源于stack exchange,提问作者MRodrigues
相关产品推荐
相关产品推荐

