Golang stdin读错了德语的小写字母



我来自德国,所以我使用像ä, öü这样的小写字母。但是Golang不能正确地从stdin中读取它们。

当我执行这个简单的程序:

package main
import (
    "bufio"
    "fmt"
    "os"
)
func main() {
    for {
        b, _, _ := bufio.NewReader(os.Stdin).ReadLine()
        printBytes(b)
    }
}
func printBytes(bytes []byte) {
    for _, b := range bytes {
        fmt.Printf("0x%X ", b)
    }
    fmt.Println()
}

我得到了输出:

C:devgolang>go run test.go
ä
0xE2 0x80 0x9E

E2 80 9E不是UTF-8中ä的正确字节序列(该工具告诉我它是"DOUBLE LOW-9引号"-> ),当我只是打印出我所读的内容时,它打印"。我写了一个小"hack",它似乎可以正确读取字符:

package main
/*
#include <stdio.h>
#include <stdlib.h>
char * getline(void) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;
    if(line == NULL)
        return NULL;
    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;
        if(--len == 0) {
            len = lenmax;
            char * linen = realloc(linep, lenmax *= 2);
            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + (line - linep);
            linep = linen;
        }
        if((*line++ = c) == 'n')
            break;
    }
    *line = '';
    return linep;
}
void freeline(char* ptr) {
    free(ptr);
}
*/
import "C"
import (
    "fmt"
    "golang.org/x/text/encoding/charmap"
)
func getLineFromCp850() string {
    line := C.getline()
    goline := C.GoString(line)
    C.freeline(line)
    b := []byte(goline)
    ub, _ := charmap.CodePage850.NewDecoder().Bytes(b)
    return string(ub)
}
func main() {
    for {
        line := getLineFromCp850()
        printBytes([]byte(line))
    }
}
func printBytes(bytes []byte) {
    for _, b := range bytes {
        fmt.Printf("0x%X ", b)
    }
    fmt.Println()
}

然后输出:

C:devgolang>go run test.go
ä
0xC3 0xA4 0xA

C3 A4ä的正确字节序列(0A是我的hack没有剥离的换行),所以看起来像是,阅读和转换从CP850到UTF-8的工作,正如我所料,但是为什么当我使用Go的功能而不是cgo读取行时,Go会给我乱码?Go有什么问题,它给了我这些值,它不把输入字节解释为CP850而是另一个字符集吗?有没有更好的Go-only方法来处理这个问题?

只有当从stdin读取时才会出现这个问题。当我打印出UTF-8 ä到stdout时,它在控制台中正确打印。

所以对于某些系统来说,这是Golang中的一个bug,特别是对于Windows系统,其中总体使用的字符集和控制台字符集是不同的(其中GetACP()GetConsoleCP()从WinAPI返回不同的东西)。例如,在德国(也许还有其他西欧国家),Windows使用代码页1252作为整体字符集,但它为控制台cmd.exe使用代码页850。不知道为什么,但就是这样。Golang错误地使用GetACP()将输入解码为UTF-8,而实际上它应该使用GetConsoleCP()返回的代码页。我们在我创建的问题中发现了这个问题,我们希望在下一个版本的Golang中看到修复。

我们还在Windows上发现了一个问题,Golang将字符解码为分解的UTF-8字符(即它将ä读取为字符a,然后是组合DIAERESIS ̈),这可能导致其他问题,例如打印这些分解的字符将它们分开打印而不是一个组合字符。

最新更新