Golang strings.EqualFold 给出了意想不到的结果

Golang strings.EqualFold gives unexpected results

在 golang (go1.17 windows/amd64) 中,下面的程序给出了以下结果:

rune1 = U+0130 'İ'
rune2 = U+0131 'ı'
lower(rune1) = U+0069 'i'
upper(rune2) = U+0049 'I'
strings.EqualFold(İ, ı) = false
strings.EqualFold(i, I) = true

我认为 strings.EqualFold 会在 Unicode 大小写折叠下检查字符串是否相等;然而,上面的例子似乎给出了一个反例。显然,两个符文都可以(手动)折叠成在大小写折叠下相等的代码点。

问题: golang strings.EqualFold(İ, ı)false 是否正确?我预计它会产生 true。如果 golang 是正确的,为什么会这样?或者这种行为是否符合某些 Unicode 规范。

我在这里错过了什么。


节目:

func TestRune2(t *testing.T) {
   r1 := rune(0x0130) // U+0130 'İ'
   r2 := rune(0x0131) // U+0131 'ı'
   r1u := unicode.ToLower(r1)
   r2u := unicode.ToUpper(r2)

   t.Logf("\nrune1 = %#U\nrune2 = %#U\nlower(rune1) = %#U\nupper(rune2) = %#U\nstrings.EqualFold(%s, %s) = %v\nstrings.EqualFold(%s, %s) = %v",
      r1, r2, r1u, r2u, string(r1), string(r2), strings.EqualFold(string(r1), string(r2)), string(r1u), string(r2u), strings.EqualFold(string(r1u), string(r2u)))
}

是的,这是“正确”的行为。这些字母在大小写折叠下表现不正常。看: http://www.unicode.org/Public/UCD/latest/ucd/CaseFolding.txt

U+0131 有全格折叠“F”和特殊“T”:

T: special case for uppercase I and dotted uppercase I
   - For non-Turkic languages, this mapping is normally not used.
   - For Turkic languages (tr, az), this mapping can be used instead
     of the normal mapping for these characters.
     Note that the Turkic mappings do not maintain canonical equivalence
     without additional processing.
     See the discussions of case mapping in the Unicode Standard for more information.

我认为没有办法强制包字符串使用 tr 或 az 映射。

来自 strings.EqualFold 来源 - unicode.ToLowerunicode.ToUpper 未使用。

相反,它使用 unicode.SimpleFold 来查看特定符文是否“可折叠”,因此可能具有可比性:

// General case. SimpleFold(x) returns the next equivalent rune > x
// or wraps around to smaller values.
r := unicode.SimpleFold(sr)
for r != sr && r < tr {
    r = unicode.SimpleFold(r)
}

符文İ不可折叠。它的小写代码点是:

r := rune(0x0130)        // U+0130 'İ'
lr := unicode.ToLower(r) // U+0069 'i'

fmt.Printf("foldable? %v\n", r != unicode.SimpleFold(r))   // foldable? false
fmt.Printf("foldable? %v\n", lr != unicode.SimpleFold(lr)) // foldable? true

如果符文不可折叠(即 SimpleFold returns 本身)——那么该符文只能匹配自身而不能匹配其他代码点。

https://play.golang.org/p/105x0I714nS