在go语言中使用io.Copy时,为什么文本编码不同?

时间:2015-09-18 02:59:23

标签: windows encoding utf-8 go windows-console

我正在尝试通过Windows上的go语言重建类似tee的util。 但我发现输出的编码并不总是一样的。

为了简化问题,我编写了这个程序:

package main

import (
    "fmt"
    "io"
    "os"
)

func main() {
    count, err := io.Copy(os.Stdout, os.Stdin)
    fmt.Println(count, err)
}

我把它命名为test。 在Windows命令控制台中,我得到了这些输出:

>test
中
中
5 <nil>

没有管道和重定向它工作正常。

>echo 中 | test
��
5 <nil>

如果我从管道获取stdin,则输出会崩溃。

>echo 中 | test > test.txt

>type test.txt
中
5 <nil>

当我将输出重定向到文件时,它再次起作用。

>test > test.txt
中

>type test.txt
荳ュ
5 <nil>

但是当我使用普通的stdin并重定向到文件时不起作用。 如果我在其他编辑器(如notepad ++)中打开此test.txt,我发现它以UTF-8编码,内容为

如果我在Windows上使用带有UTF-8编码控制台的Cygwin,一切都很好。

从输出中,我知道程序复制的字节数是5,这意味着无论stdin是什么,它都在程序中使用UTF-8。 但是我知道windows命令行控制台基本上是使用非unicode编码,为什么它被转换成UTF-8?有没有办法让程序只是复制stdin发送的内容而不进行任何转换?

顺便说一句。如果我使用gnuWin32中的tee进行同样的测试,一切都运行良好。

>where tee
D:\Tools\gnuWin32\bin\tee.exe

>echo 中 | tee
中

>tee tee.txt
中
中
^C
>type tee.txt
中

有没有人知道这个的原因以及解决方案是什么?

1 个答案:

答案 0 :(得分:0)

它不使用utf8,为什么写入5个字节是因为中间有一个空格(0x20)

C:\Users\jan>echo 中| go run src/main.go
00000000  d6 d0 0d 0a                                       |....|
��
4 <nil>

所以在我的系统中,控制台不使用utf8,而是使用GBK。

该错误是因为Windows控制台无法更改屏幕上的字符,即使附加的字节使字符成为另一个字符。例如&#39; d6 d0&#39;是中,d6已在屏幕上显示为�,0a附加,不使两个字节成为一个显示字符。

进行测试,我有一个c#控制台程序

static void Main(string[] args)
    {

        using (Stream stdout = Console.OpenStandardOutput())
        {
            stdout.WriteByte((byte)'A');
            stdout.WriteByte(0xd6);
            stdout.WriteByte(0xd0);
        }

        using (Stream stdout = Console.OpenStandardOutput())
        {
            stdout.WriteByte((byte)'B');
            stdout.WriteByte(0xd6);

        }

        using (Stream stdout = Console.OpenStandardOutput())
        {
            stdout.WriteByte(0xd0);
        }
    }

得到结果:

A中BPress any key to continue . . .

所以我猜windows libc在stdout之前有一个缓冲区,它组成两个字节是一个字符并打印到控制台。

我发现有趣的是,即使windows控制台在gbk页面中,go lang也可以使用utf8编码编写stdou。似乎字节写入os.Stdout并没有直接传递给控制台。

package main

import (
    "fmt"
    "os"
)

func main() {
    os.Stdout.Write([]byte{0xe4,0xb8,0xad})
    fmt.Print("\xe4")
    fmt.Print("\xb8")
    fmt.Println("\xad")
}

得到:

C:\Users\jan>go run src/main.go
中中

C:\Users\jan>