使用golang.org/x/text/collate进行Unicode排序

使用golang.org/x/text/collate进行Unicode排序 我正在实现一个用于数据表示的库。 参考:https://github.com/bnclabs/gson

该包支持的功能之一是将复合数据(支持JSON)编译成可使用memcmp进行排序的二进制格式。

作为这个二进制排序功能的一部分,我需要支持基于ICU排序标准的字符串排序。经过一番搜索后,发现了这个很棒的包:

Package collate

Package collate contains types for comparing and sorting Unicode strings according to a given collation order.

我正在使用Collator.Key()将字符串编译成可与memcmp一起使用的ICU排序键。

为此编写了几个测试用例,效果良好。

我的问题是:

将字符串值编译成二进制可比较的排序键后,我能否从其排序键中恢复原始字符串值?这是ICU标准的限制还是golang.org/x/text/collate的限制?

谢谢,


更多关于使用golang.org/x/text/collate进行Unicode排序的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

我也遇到了同样的问题。一个可能的解决方案是在"编码"阶段将原始字符串与排序键一起存储(可能使用已知分隔符分隔,或作为元组存储),然后在"解码"阶段获取原始字符串。然而,这会增加额外的空间开销。

正如您已经提到的,我无法找到从排序键到原始字符串的反向映射。通过快速浏览代码,看起来 GoLang 的 text 包使用 CGo 调用 libicu,而 libicu 本身没有提供反向映射功能。

理想情况下,希望有排序规则/语言方面的专家能够就这种情况下应遵循的最佳实践给我们提供建议!

更多关于使用golang.org/x/text/collate进行Unicode排序的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


承接我之前的评论,这里提供一些相关链接来佐证您实际上需要同时存储排序键和原始数据——似乎不存在能够从排序键反向映射回原始键的逆函数。

scooz

反转Unicode字符串排序键

标签: unicode, collation, icu, uca


cockroachdb/cockroach/blob/500123d16456a9044bad86a7934a45faaafcbd55/docs/tech-notes/encoding.md

Structured data encoding in CockroachDB SQL
===========================================

Like many databases, CockroachDB (CRDB) encodes SQL data into key-value
(KV) pairs. The format evolves over time, with an eye toward backward
compatibility. This document describes format version 3 in detail except
for how CRDB encodes primitive values ([pkg/util/encoding/encoding.go]).

The Cockroach Labs blog post [SQL in CockroachDB: Mapping Table Data to
Key-Value Storage] covers format version 1, which predates column
families, interleaving, and composite encoding. Format version 2
introduced column families, covered in [Implementing Column Families in
CockroachDB]. See also the [column families RFC] and the [interleaving
RFC].

This document was originally written by David Eisenstat
<<eisen@cockroachlabs.com>>.

Tables (primary indexes)
------------------------

此文件内容已被截断。查看原文

感谢, Aman Achpal

在 Unicode 排序中,使用 golang.org/x/text/collateCollator.Key() 方法生成的排序键是单向转换,无法从排序键恢复原始字符串。这是由 ICU 排序键的设计决定的,而不是 Go 语言包的限制。排序键的目的是为了高效的二进制比较,而不是存储原始数据。

排序键是原始字符串的规范化表示,经过压缩和编码,丢失了部分信息。例如,不同字符串可能生成相同的排序键(在特定排序规则下被视为相等),因此反向恢复不可行。

以下是一个示例,展示排序键的生成和比较,并验证反向恢复的不可行性:

package main

import (
	"fmt"
	"golang.org/x/text/collate"
	"golang.org/x/text/language"
)

func main() {
	// 创建 Collator,使用英语排序规则
	cl := collate.New(language.English)

	// 原始字符串
	str1 := "cafe"
	str2 := "café"

	// 生成排序键
	key1 := cl.KeyFromString(nil, str1)
	key2 := cl.KeyFromString(nil, str2)

	// 比较排序键
	fmt.Printf("Key1: %x\n", key1)
	fmt.Printf("Key2: %x\n", key2)
	fmt.Printf("Comparison result: %d\n", collate.Compare(key1, key2))

	// 尝试从排序键恢复字符串 - 无法实现
	// 没有提供反向方法,如 key.ToString()
}

输出示例(具体键值可能因版本和规则不同):

Key1: 2929292b01
Key2: 2929292b0101
Comparison result: -1

如果你需要同时保留原始字符串和排序键,必须在数据结构中存储原始字符串的副本。例如:

type SortableString struct {
	Original string
	SortKey  []byte
}

func NewSortableString(cl *collate.Collator, s string) SortableString {
	return SortableString{
		Original: s,
		SortKey:  cl.KeyFromString(nil, s),
	}
}

这样,你可以通过 SortKey 进行二进制比较,同时通过 Original 访问原始值。

回到顶部