我来自德国,所以我使用像ä
, ö
和ü
这样的小写字母。但是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 ̈
),这可能导致其他问题,例如打印这些分解的字符将它们分开打印而不是一个组合字符。