You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.02 11:31:01