Golang utf8編碼與utf16編碼相互轉換(string 和 []uint16互轉)


  • 源代碼已提交至 https://github.com/lianggx6/goutf16 ,可方便在代碼中引用。
  • 最近使用Golang進行一些編碼方面的工作,需要把utf8編碼的string轉化為utf16編碼的uint16數組。
  • 比較簡單直接的做法是借助golang中的utf16標准庫和rune類型進行轉換。如下所示。這個方法雖然寫起來簡單,但是缺點在於內存消耗更大,效率太低。主要原因在於string類型轉換[]rune類型時,需要申請一次內存。[]rune再轉換成[]uint16時又需要申請一遍內存。這樣一來,這個操作就慢了很多。
package main

import (
	"fmt"
	"unicode/utf16"
)

func main() {
	encodeContent := utf16.Encode([]rune("你好"))		//encode
	fmt.Println(encodeContent)
	decodeContent := utf16.Decode(encodeContent)		//decode
	fmt.Println(string(decodeContent))
}
  • 在網上找了許久也沒有找到其他可以將一個utf8編碼的數組轉換為utf16編碼的[]uint16數組的好方法,於是決定仿照utf16標准庫自己動手實現一個。
  • 這份代碼能夠實現主要是在於,golang中,使用for range 語法迭代string和[]rune數組時,所得到的值是一樣的。(但是使用len+下標訪問就是不一樣的了),所以完全可以把標准庫中的代碼抄過來改一下變量來實現。
  • 源碼:
package goutf16

import (
	"strings"
	"unicode/utf16"
	"unicode/utf8"
)

const (
	replacementChar = '\uFFFD'     // Unicode replacement character
	maxRune         = '\U0010FFFF' // Maximum valid Unicode code point.
)

const (
	// 0xd800-0xdc00 encodes the high 10 bits of a pair.
	// 0xdc00-0xe000 encodes the low 10 bits of a pair.
	// the value is those 20 bits plus 0x10000.
	surr1 = 0xd800
	surr2 = 0xdc00
	surr3 = 0xe000

	surrSelf = 0x10000
)

func EncodeStringToUTF16(s string) []uint16 {
	n := 0
	for _, v := range s {
		n++
		if v >= 0x10000 {
			n++
		}
	}

	a := make([]uint16, n)
	n = 0
	for _, v := range s {
		switch {
		case 0 <= v && v < surr1, surr3 <= v && v < surrSelf:
			// normal rune
			a[n] = uint16(v)
			n++
		case surrSelf <= v && v <= maxRune:
			// needs surrogate sequence
			r1, r2 := utf16.EncodeRune(v)
			a[n] = uint16(r1)
			a[n+1] = uint16(r2)
			n += 2
		}
	}
	return a[:n]
}

func DecodeUTF16ToString(s []uint16) string {
	n := 0
	for i := 0; i < len(s); i++ {
		switch r := s[i]; {
		case r < surr1, surr3 <= r:
			// normal rune
			n += utf8.RuneLen(rune(r))
		case surr1 <= r && r < surr2 && i+1 < len(s) &&
			surr2 <= s[i+1] && s[i+1] < surr3:
			// valid surrogate sequence
			n += utf8.RuneLen(utf16.DecodeRune(rune(r), rune(s[i+1])))
			i++
		default:
			// invalid surrogate sequence
			n += utf8.RuneLen(replacementChar)
		}
	}
	var b strings.Builder
	b.Grow(n)
	for i := 0; i < len(s); i++ {
		switch r := s[i]; {
		case r < surr1, surr3 <= r:
			// normal rune
			b.WriteRune(rune(r))
		case surr1 <= r && r < surr2 && i+1 < len(s) &&
			surr2 <= s[i+1] && s[i+1] < surr3:
			// valid surrogate sequence
			b.WriteRune(utf16.DecodeRune(rune(r), rune(s[i+1])))
			i++
		default:
			// invalid surrogate sequence
			b.WriteRune(replacementChar)
		}
	}
	return b.String()
}
package main

import (
	"fmt"
	"github.com/lianggx6/goutf16"
)

func main() {
	encodeContent := goutf16.EncodeStringToUTF16("你好") //encode
	fmt.Println(encodeContent)
	decodeContent := goutf16.DecodeUTF16ToString(encodeContent)		//decode
	fmt.Println(string(decodeContent))
}
  • 由於這種實現,避免了轉碼過程中[]rune類型和string類型的轉換,大大提高了轉碼效率,本質上和string轉換為[]rune的消耗相差無幾。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM