使用Go语言的`for`循环作为`while`循环的方式。

huangapple go评论84阅读模式
英文:

Using Go 'for' loop as 'while'

问题

我正在尝试将一个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字符串(不兼容Latin-1,有3个不兼容的符文)
	t := charmap.ISO8859_1.NewEncoder()                        // 转换器,用于转换为Latin-1

	ini := 0          // ini用于指定要分析的字符串的初始位置
	strISO88591 := "" // 初始化兼容Latin-1的字符串
	counter := 0      // 设置一个计数器,在循环的前5次迭代后强制退出('for'作为'while'使用时不按预期工作)

	err := errors.New("initiate err with non-nil value") // 将err初始化为非nil值,以便进入循环

	for err != nil { // 当'err != nil'为false时,循环应该退出,即当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()

		// 使用以下3行代码可以正常工作(为什么在'for'语句中不行???)
		//if err == nil {
		//	break
		//}

		// 设置一个计数器,在循环的前5次迭代后强制退出
		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

正如我们所看到的,第4次迭代后,'err!=nil'被评估为'false',所以我期望它退出'for err != nil'循环,但它从未这样做(直到我使用计数器强制中断)。
'for'不应该像其他语言的'while'一样工作吗?我做错了什么吗?

规格:
Go版本:go1.19.5 windows/amd64

英文:

I am trying to transform a UTF-8 string into a Latin-1 enabled string (converting all illegal Latin-1 chars into a '?') in order to save it into a txt file to be used in a Latin-1-only system.

For test purpose I used this code:

package main
import (
&quot;errors&quot;
&quot;fmt&quot;
&quot;os&quot;
&quot;golang.org/x/text/encoding/charmap&quot;
&quot;golang.org/x/text/transform&quot;
)
func main() {
strUTF8 := &quot;example 1: Г, example 2: ≤, example 3: “, etc&quot; // utf-8 string to be converted (not Latin-1 compatible, 3 uncompatibles runes)
t := charmap.ISO8859_1.NewEncoder()                        // transformer to Latin-1
ini := 0          // ini establishes the initial position of the string to be analized
strISO88591 := &quot;&quot; // initiate Latin-1 compatible string
counter := 0      // put a counter to forcibly break after 5 iters of the loop (&#39;for&#39; as a &#39;while&#39; is not working as expected)
err := errors.New(&quot;initiate err with non-nil value&quot;) // initiate err with non-nil value to enter the loop
for err != nil { // loop should exit for when &#39;err != nil&#39; evaluates to false, that is, when 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])) //initial position of string in next iter should jump chars already converted + not allowed rune
str = str + &quot;?&quot;
}
strISO88591 = strISO88591 + str
// prints to display results
fmt.Println(&quot;sISO88591:&quot;, strISO88591)
fmt.Println(&quot;err:&quot;, err)
fmt.Println(&quot;err!=nil:&quot;, err != nil)
fmt.Println()
// with the following 3 lines below it works (why not in the &#39;for&#39; statement????)
//if err == nil {
//	break
//}
// put a counter to forcibly break after 5 iters of the loop
counter += 1
if counter &gt; 4 {
fmt.Println(&quot;breaking forcibly&quot;)
break
}
}
f, _ := os.Create(&quot;test.txt&quot;)
defer f.Close()
_, err = f.WriteString(strISO88591)
if err != nil {
panic(err)
}
}

That code prints in the terminal:

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: &lt;nil&gt;
err!=nil: false
sISO88591: example 1: ?, example 2: ?, example 3: ?, etc, etc
err: &lt;nil&gt;
err!=nil: false
breaking forcibly

As we can see, after 4th iteration 'err!=nil' is evaluated to 'false' and so I expected it to exit the 'for err != nil' loop, but it never does (until I forcibly broke it with the help of a counter) .
Isn´t 'for' supposed to work as other languages 'while'? Am I doing something wrong?

Specs:
Go version: go1.19.5 windows/amd64

答案1

得分: 4

你正在重新声明 err

for err != nil {  // 这里使用了之前声明的 err
      str, n, err := transform.String(t, strUTF8[ini:]) // 这里重新声明并遮蔽了之前的 err
 ...

你可以通过以下方式解决这个问题:

for err != nil {
      var str string
      var n int
      str, n, err = transform.String(t, strUTF8[ini:]) // 这里重新声明并遮蔽了之前的 err
 ...
英文:

You are redeclaring err:

for err != nil {  // This is using err declared before
str, n, err := transform.String(t, strUTF8[ini:]) // This is redeclaring and shadowing previous err
...

You can deal with it by:

for err != nil {
var str string
var n int
str, n, err = transform.String(t, strUTF8[ini:]) // This is redeclaring and shadowing previous err
...
</details>

huangapple
  • 本文由 发表于 2023年2月3日 02:35:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75327550.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定