我可以将文件读取为字节数组
但是当我将其转换为字符串时
它将utf16字节视为ASCII
如何正确转换?
package main import ("fmt" "os" "bufio" ) func main(){ // read whole the file f, err := os.Open("test.txt") if err != nil { fmt.Printf("error opening file: %v\n",err) os.Exit(1) } r := bufio.NewReader(f) var s,b,e = r.ReadLine() if e==nil{ fmt.Println(b) fmt.Println(s) fmt.Println(string(s)) } }
输出:
假
[255 254 91 0 83 0 99 0 114 0 105 0 112 0 116 0 32 0 73 0 110 0 102 0 111 0 93 0 13 0]
Script I nfo]
更新:
在测试了两个示例之后,我已经了解了确切的问题。
在Windows中,如果我在行尾添加换行符(CR + LF),则会在该行中读取CR。因为readline函数不能正确处理unicode([OD OA] = ok,[OD 00 OA 00] =不正常)。
如果readline函数可以识别unicode,则它应该理解[OD 00 OA 00]并返回[] uint16而不是[] bytes。
所以我认为我不应该使用bufio.NewReader,因为它无法读取utf16,我看不到bufio.NewReader.ReadLine可以接受参数作为标志来指示读取文本是utf8,utf16le / be或utf32。go库中是否有用于unicode文本的readline函数?
UTF16,UTF8和字节顺序标记由Unicode联合会定义:UTF-16常见问题解答,UTF-8常见问题解答和字节顺序标记(BOM)常见问题解答。
问题4802:bufio:阅读行太繁琐 从文件中读取行在Go中太麻烦了。 人们常常会因为其名称而将其吸引到bufio.Reader.ReadLine,但是它具有一个奇怪的签名,并且会返回(行[] byte,isPrefix bool,err错误),并且需要进行大量工作。 ReadSlice和ReadString需要定界符字节,这几乎总是明显且难看的’\ n’,并且还可以返回一行和一个EOF
问题4802:bufio:阅读行太繁琐
从文件中读取行在Go中太麻烦了。
人们常常会因为其名称而将其吸引到bufio.Reader.ReadLine,但是它具有一个奇怪的签名,并且会返回(行[] byte,isPrefix bool,err错误),并且需要进行大量工作。
ReadSlice和ReadString需要定界符字节,这几乎总是明显且难看的’\ n’,并且还可以返回一行和一个EOF
修订:f685026a2d38 bufio:新的扫描仪界面 根据称为“扫描仪”的新类型,添加一个新的简单界面来扫描(可能是文本)数据。它有自己的内部缓冲,因此即使没有注入bufio.Reader也应该有效率。输入的格式由“拆分功能”定义,默认情况下分为几行。
修订:f685026a2d38
bufio:新的扫描仪界面
根据称为“扫描仪”的新类型,添加一个新的简单界面来扫描(可能是文本)数据。它有自己的内部缓冲,因此即使没有注入bufio.Reader也应该有效率。输入的格式由“拆分功能”定义,默认情况下分为几行。
go1.1beta1发布 您可以从通常的位置下载二进制和源分发版:https : //code.google.com/p/go/downloads/list?q=go1.1beta1
go1.1beta1发布
您可以从通常的位置下载二进制和源分发版:https : //code.google.com/p/go/downloads/list?q=go1.1beta1
这是一个使用Unicode规则将UTF16文本文件行转换为Go UTF8编码的字符串的程序。该代码已经过修改,以利用bufio.ScannerGo 1.1 中的新界面。
bufio.Scanner
package main import ( "bufio" "bytes" "encoding/binary" "fmt" "os" "runtime" "unicode/utf16" "unicode/utf8" ) // UTF16BytesToString converts UTF-16 encoded bytes, in big or little endian byte order, // to a UTF-8 encoded string. func UTF16BytesToString(b []byte, o binary.ByteOrder) string { utf := make([]uint16, (len(b)+(2-1))/2) for i := 0; i+(2-1) < len(b); i += 2 { utf[i/2] = o.Uint16(b[i:]) } if len(b)/2 < len(utf) { utf[len(utf)-1] = utf8.RuneError } return string(utf16.Decode(utf)) } // UTF-16 endian byte order const ( unknownEndian = iota bigEndian littleEndian ) // dropCREndian drops a terminal \r from the endian data. func dropCREndian(data []byte, t1, t2 byte) []byte { if len(data) > 1 { if data[len(data)-2] == t1 && data[len(data)-1] == t2 { return data[0 : len(data)-2] } } return data } // dropCRBE drops a terminal \r from the big endian data. func dropCRBE(data []byte) []byte { return dropCREndian(data, '\x00', '\r') } // dropCRLE drops a terminal \r from the little endian data. func dropCRLE(data []byte) []byte { return dropCREndian(data, '\r', '\x00') } // dropCR drops a terminal \r from the data. func dropCR(data []byte) ([]byte, int) { var endian = unknownEndian switch ld := len(data); { case ld != len(dropCRLE(data)): endian = littleEndian case ld != len(dropCRBE(data)): endian = bigEndian } return data, endian } // SplitFunc is a split function for a Scanner that returns each line of // text, stripped of any trailing end-of-line marker. The returned line may // be empty. The end-of-line marker is one optional carriage return followed // by one mandatory newline. In regular expression notation, it is `\r?\n`. // The last non-empty line of input will be returned even if it has no // newline. func ScanUTF16LinesFunc(byteOrder binary.ByteOrder) (bufio.SplitFunc, func() binary.ByteOrder) { // Function closure variables var endian = unknownEndian switch byteOrder { case binary.BigEndian: endian = bigEndian case binary.LittleEndian: endian = littleEndian } const bom = 0xFEFF var checkBOM bool = endian == unknownEndian // Scanner split function splitFunc := func(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil } if checkBOM { checkBOM = false if len(data) > 1 { switch uint16(bom) { case uint16(data[0])<<8 | uint16(data[1]): endian = bigEndian return 2, nil, nil case uint16(data[1])<<8 | uint16(data[0]): endian = littleEndian return 2, nil, nil } } } // Scan for newline-terminated lines. i := 0 for { j := bytes.IndexByte(data[i:], '\n') if j < 0 { break } i += j switch e := i % 2; e { case 1: // UTF-16BE if endian != littleEndian { if i > 1 { if data[i-1] == '\x00' { endian = bigEndian // We have a full newline-terminated line. return i + 1, dropCRBE(data[0 : i-1]), nil } } } case 0: // UTF-16LE if endian != bigEndian { if i+1 < len(data) { i++ if data[i] == '\x00' { endian = littleEndian // We have a full newline-terminated line. return i + 1, dropCRLE(data[0 : i-1]), nil } } } } i++ } // If we're at EOF, we have a final, non-terminated line. Return it. if atEOF { // drop CR. advance = len(data) switch endian { case bigEndian: data = dropCRBE(data) case littleEndian: data = dropCRLE(data) default: data, endian = dropCR(data) } if endian == unknownEndian { if runtime.GOOS == "windows" { endian = littleEndian } else { endian = bigEndian } } return advance, data, nil } // Request more data. return 0, nil, nil } // Endian byte order function orderFunc := func() (byteOrder binary.ByteOrder) { switch endian { case bigEndian: byteOrder = binary.BigEndian case littleEndian: byteOrder = binary.LittleEndian } return byteOrder } return splitFunc, orderFunc } func main() { file, err := os.Open("utf16.le.txt") if err != nil { fmt.Println(err) os.Exit(1) } defer file.Close() fmt.Println(file.Name()) rdr := bufio.NewReader(file) scanner := bufio.NewScanner(rdr) var bo binary.ByteOrder // unknown, infer from data // bo = binary.LittleEndian // windows splitFunc, orderFunc := ScanUTF16LinesFunc(bo) scanner.Split(splitFunc) for scanner.Scan() { b := scanner.Bytes() s := UTF16BytesToString(b, orderFunc()) fmt.Println(len(s), s) fmt.Println(len(b), b) } fmt.Println(orderFunc()) if err := scanner.Err(); err != nil { fmt.Println(err) } }
utf16.le.txt 15 "Hello, 世界" 22 [34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 0 22 78 76 117 34 0] 0 0 [] 15 "Hello, 世界" 22 [34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 0 22 78 76 117 34 0] LittleEndian utf16.be.txt 15 "Hello, 世界" 22 [0 34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 78 22 117 76 0 34] 0 0 [] 15 "Hello, 世界" 22 [0 34 0 72 0 101 0 108 0 108 0 111 0 44 0 32 78 22 117 76 0 34] BigEndian