go x/text/collate: 斯洛伐克语单词排序错误

wd2eg0qa  于 6个月前  发布在  Go
关注(0)|答案(7)|浏览(42)

你正在使用的Go版本是什么(go version)?

$ go version
go version go1.17 linux/amd64

这个问题在最新版本中是否重现?
是的。
你正在使用什么操作系统和处理器架构(go env)?
go env 输出

$ go env
GO111MODULE=""
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/jano7/.cache/go-build"
GOENV="/home/jano7/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/jano7/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/jano7/go"
GOPRIVATE=""
GOPROXY="https://proxy.golang.org,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.17"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/jano7/Documents/prog/golang/tmp/sortwords/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build2259433300=/tmp/go-build -gno-record-gcc-switches"

你做了什么?

package main

import (
	"fmt"

	"golang.org/x/text/collate"
	"golang.org/x/text/language"
)

func main() {

	words := []string{"čaj", "auto", "pot", "márny", "kľak", "chyba", "drevo",
		"cibuľa", "džíp", "džem", "šum", "pól", "čučoriedka", "banán", "čerešňa",
		"červený", "klam", "čierny", "tŕň", "pôst", "hôrny", "mat", "chobot",
		"cesnak", "kĺb", "mäta", "ďateľ", "troska", "sýkorka", "elektrón",
		"fuj", "zem", "guma", "hora", "gejzír", "ihla", "pýr", "hrozno",
		"jazva", "džavot", "lom"}

	c := collate.New(language.Slovak)
	c.SortStrings(words)

	fmt.Println(words)
}
$ go run sort_accented_words.go 
[auto banán cesnak cibuľa čaj čerešňa červený čierny čučoriedka ďateľ drevo džavot džem 
džíp elektrón fuj gejzír guma hora hôrny hrozno chobot chyba ihla jazva kľak klam kĺb lom 
márny mat mäta pól pot pôst pýr sýkorka šum tŕň troska zem]

这个解决方案并不是100%正确(但与其他语言(如Java、C#或Python)相比仍然相当不错)。
从我测试过的语言来看,Raku和Go提供了最接近的解决方案。(Raku处理重音符号正确,但对双音节词处理失败。)
以下是斯洛伐克语中字符的正确顺序:
a, á, ä, b, c, č, d, ď, dz, dž, e, é, f, g, h, ch, i, í, j, k, l, ĺ, ľ, m, n, ň, o, ó, ô, p, q, r, ŕ, s, š, t, ť, u, ú, v, w, x, y, ý, z, ž
注意普通字符总是在重音字符之前。所以单词ďateľ应该在drev后面,序列kľak klam kĺb应该是klam kĺb kľak,márny mat mäta应该是mat márny mäta,pól pot pôst应该是pot pól pôst,以及tŕň troska应该是troska tŕň。
带有双音节词ch的单词是正确的。(dž双音节词可能也很好。不是100%确定,因为有一个关于ď的问题。)
所以正确的顺序应该是:

$ go run sort_accented_words.go 
[auto banán cesnak cibuľa čaj čerešňa červený čierny čučoriedka drevo ďateľ džavot džem 
džíp elektrón fuj gejzír guma hora hôrny hrozno chobot chyba ihla jazva klam kĺb kľak lom 
mat márny mäta pot pól pôst pýr sýkorka šum troska tŕň zem]
nvbavucw

nvbavucw1#

Go语言的字符串排序并不是按照任何语言中事物排序的方式。它只是对utf8编码的原始字节进行排序。
你可能需要使用golang.org/x/text/collate库。有关示例,请参阅https://webdevstation.com/posts/how-to-sort-strings-with-go-alphabetically-in-any-language/

rkttyhzu

rkttyhzu2#

对不起,你正在使用那个。我想这是x/text/collate的一个bug?

9cbw7uwe

9cbw7uwe3#

是的,看起来像是x/text/collate

0kjbasz6

0kjbasz65#

你好,
我尝试研究了一下这个问题,但没有深入到足够深的兔子洞中去修复它。以下是我发现的内容(系好安全带,这是一次漫长的旅程):
首先我想从这个事实开始,字母表在这里似乎被正确定义了。所以这不是问题所在。

collate pkg 使用这个unicode算法对单词进行排序:

主要算法有四个步骤:

  1. 对每个输入字符串进行规范化。
  2. 为每个字符串生成一个排序元素数组。
  3. 从排序元素数组为每个字符串生成一个排序键。
  4. 使用二进制比较操作符比较两个排序键。

字母 č 可以正确排序。

  • 对于 č ,算法会产生一个包含一个排序元素( 0x402c3c20 )的数组。
  • 这个元素具有主权重 5662 、次权重 32 、三级权重 2
  • 这会产生一个排序字符串 [22 30 0 0 0 32 0 0 2]
    字母 ď 不会正确排序。
  • 对于 ď ,算法会产生一个包含一个排序元素( 0xe0000a83 )的数组。代码。
  • 然后,由于这个值,这个元素被分成两个元素:一个用于字母 d ( 0x402c6220 )和重音符号 ( 0xae604102 )。代码。
  • 没有重音符号的字母 d (没有重音符号)具有主权重 5681 、次权重 32 、三级权重 2
  • 重音符号具有主权重 0 、次权重 65 、三级权重 2
  • 为了形成排序字符串,所有的主要权重都被添加到一个数组中,然后是所有的次要权重,然后是所有的三级权重。如果值为0,则跳过它。
  • 这会产生一个排序字符串 [22 49 0 0 0 32 0 65 0 0 2 2]
  • 将一个元素分成两个的效果是将 d 视为几乎相同的与 ď
  • 如果你对它们进行排序: 你可以看到你得到的是 [d da dat d ď ďat] ,它将被视为等于 [d ď da dat ďat dr] ,这就好像 d' == d 一样。
    更深入的例子

对于 ďat 的排序字符串是: [22 49 21 239 24 22 0 0 0 32 0 65 0 32 0 32 0 0 2 2 2 2] :
|-----------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------------|------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |------- |

hsgswve4

hsgswve46#

@ameowlia,感谢你的分析!遗憾的是,我对x/text/collate的了解非常有限,所以我不确定我能否提供有用的建议来指导接下来的工作。
不过,我有一个问题:元素拆分问题是否存在于Unicode TR10算法本身,还是Go实现偏离了该算法?(如果问题存在于TR10算法本身,那么Go项目不能单方面解决它...)

qf9go6mv

qf9go6mv7#

你好@bcmills,
Unicode TR10算法中是否存在元素拆分问题,还是Go实现与算法有所偏离?
🐛 我认为这是Go中的一个bug
在第7.5节中,它使用了以下示例:
... the following incorrect ordering would result: "aa" < "àa" < "ab"
... the following correct ordering would result: "aa" < "ab" < "àa"
这正是我上面描述的错误结果。我认为这一节描述的是另一个(但可能相关的问题,而不是我们在这里遇到的问题。然而,我的观点是,文档明确指出我们得到的结果是不正确的。
🇸🇰 它特别提到了 ch
ch 在斯洛伐克语中被认为是一个字符,排在 h 之后,排在 i 之前。在第3.3.3节缩略词中,文档说:
同样,当序列ch被视为单个双字母时,例如在斯洛伐克语中,它被表示为从两个字符到单个排序元素的Map,如下所示。...
在这个例子中,排序元素[.1D19.0020.0002]的主权重比单独的字母h的主权重大一,因此ch将排在h之后,i之前。这个例子展示了对排序元素Map进行定制以将字母作为单个单元的权重序列的结果。
上面我说问题出在 d' does not have a different primary value from d 上。我认为这一节的文档有助于证实不同的字母应该具有不同的主值。而且因为斯洛伐克语将带有重音的字母视为不同的字母,所以它们应该具有不同的主值。
👀 Go哪里出错了?

  • 扩展和收缩元素并不一定是错误的,但我认为在这种情况下是错误的。
  • 为了使字母按顺序排列,以便 "aa" < "ab" < "àa"aàb 都需要不同的主值。
  • 对于斯洛伐克语的许多带有重音的字母来说,这一点并没有发生。

开放性问题

  • 我不知道从哪里开始修复这个问题。我有一些问题:
  • 这些尝试是如何进行的?
  • 它们是否与unicode中的某个表完全相同?
  • Go负责的范围和unicode提供的范围之间有什么区别?

相关问题