```go unicode/utf16: 添加一个示例,说明如何使用utf16.DecodeRune, ```

1hdlvixo  于 6个月前  发布在  Go
关注(0)|答案(3)|浏览(43)

Go版本
go版本 go1.21.4 linux/amd64

在你的模块/工作区中,go env 的输出是什么?

N/A

你做了什么?

打开了https://pkg.go.dev/unicode/utf16#DecodeRune

你在页面上看到了什么?

页面上没有示例。

你期望看到什么?

一个使用DecodeRune来解码一个 []uint16 的例子,而不需要进行堆分配,类似于 utf8.DecodeRune 的工作方式。

cgh8pdjw

cgh8pdjw1#

根据我对包的理解,这是我为了将字符串从utf8编码转换为utf16编码而编写的代码。我不得不深入研究utf16包的内部来编写这段代码,并复制粘贴了一些内容,尤其是第一个函数。
将它们留在这里,因为它们将是很好的补充,以展示如何使用utf16包与其更常用的对应项utf8一起使用。

func encodeUTF16to8(dstUTF8, srcUTF16 []byte, order16 binary.ByteOrder) (int, error) {
	// UTF16 values.
	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
	)
	n := 0
	var r1, r2 rune
	for {
		slen := len(srcUTF16)
		if slen == 0 {
			break
		}
		r1 = rune(order16.Uint16(srcUTF16))
		if slen >= 4 {
			r2 = rune(order16.Uint16(srcUTF16[2:]))
		}
		var ar rune
		switch {
		case r1 < surr1, surr3 <= r1:
			// normal rune
			ar = r1
			srcUTF16 = srcUTF16[2:]
		case surr1 <= r1 && r1 < surr2 && slen >= 4 &&
			surr2 <= r2 && r2 < surr3:
			// valid surrogate sequence
			ar = utf16.DecodeRune(r1, r2)
			srcUTF16 = srcUTF16[4:]
		default:
			// invalid surrogate sequence
			return n, errors.New("invalid utf16")
		}
		// Encode the rune into UTF-8.
		if utf8.RuneLen(ar) > len(dstUTF8[n:]) {
			return n, errors.New("insufficient utf8 buffer")
		}
		n += utf8.EncodeRune(dstUTF8[n:], ar)
	}
	return n, nil
}

func encodeUTF8to16(dst16, src8 []byte, order16 binary.ByteOrder) (int, error) {
	n := 0
	for len(src8) > 0 {
		r1, size := utf8.DecodeRune(src8)
		src8 = src8[size:]
		switch {
		case utf16.IsSurrogate(r1):
			// Surrogate pair case.
			if len(dst16) < 4 {
				return n, errors.New("insufficient utf16 buffer")
			}
			r1, r2 := utf16.EncodeRune(r1)
			order16.PutUint16(dst16[n:], uint16(r1))
			order16.PutUint16(dst16[n+2:], uint16(r2))
			n += 4
		default:
			// General case.
			if len(dst16) < 2 {
				return n, errors.New("insufficient utf16 buffer")
			}
			// Simplest case for ASCII characters.
			order16.PutUint16(dst16[n:], uint16(r1))
			n += 2
		}
	}
	return n, nil
}
zzlelutf

zzlelutf2#

尽管我理解您希望避免堆分配,但所有使用unicode/utf16的情况都会将此包返回的[]rune显式转换为字符串。这样做既简单又快速,代码量很少。如果那里存在瓶颈,我希望在实际应用中看到它。
unicode/utf8包没有像您建议的那种跨转换,虽然公平地说,它实际上并不需要它们,因为该语言直接支持该编码。
简而言之,似乎没有必要为您建议的例程添加到库中。
我相信示例会很好,但它们应该展示出每个人似乎都在使用的惯用转换,而不是您在这里展示的复杂代码。

snz8szmq

snz8szmq3#

为了澄清我措辞不当的评论:我的意思是将例程添加到pkg.go.dev中。
结果证明,在“真实”的Go代码中并没有瓶颈。正如你所说,Go的垃圾回收器是业界最先进的,进行显而易见的转换很可能效果很好。问题在于在微控制器上使用TinyGo分配内存时,RAM非常有限,内存容易碎片化,最终导致程序崩溃。
我理解TinyGo不是Go,一个更优雅的解决方案是在TinyGo中创建一个更强大的垃圾回收器,但这是一个艰巨的任务。
总之,虽然我不建议将这些utf16-utf8转换例程作为包的一部分,而是作为使用示例,但我非常希望暴露utf16内部的某个部分。我已经在这里提出了一个提案:#65511
编辑:我注意到,将#65511中提议的例程添加到其中会极大地简化其中一个转换函数:

func encodeUTF16to8(dstUTF8, srcUTF16 []byte, order16 binary.ByteOrder) (int, error) {
	n := 0
	for len(srcUTF16) > 1 {
		r, size := utf16.DecodeBytes(srcUTF16, order16)
		if r == utf8.RuneError {
			return n, errors.New("invalid utf16 sequence")
		}
		srcUTF16 = srcUTF16[size:]
		n += utf8.EncodeRune(dstUTF8[n:], r)
	}
	return n, nil
}

相关问题