【问题标题】:How to create a case insensitive map in Go?如何在 Go 中创建不区分大小写的地图?
【发布时间】:2012-06-20 17:12:41
【问题描述】:

我想要一个不敏感的字符串作为键。 它是由语言支持还是我必须自己创建? 谢谢

编辑:我正在寻找的是一种默认方式,而不是每次使用地图时都必须记住转换键。

【问题讨论】:

  • 每次手动映射到 Unicode foldcase。
  • SCL,在这种情况下,您是否关心 Unicode?也就是说,您的需求是否包括拒绝意外的 Unicode 代码点或小心处理预期的 Unicode 代码点?
  • @sonia,嗨,我只考虑 ASCII。但既然你问,我将如何处理 Unicode?​​span>
  • @SCL 对于非 ASCII,你有一个问题,因为据我所知,Go 没有提供 toFoldcase 映射来实现这一点。 Sonya 的代码只能在 ASCII 上工作,但在 Unicode 上就搞砸了。
  • 我了解存在许多问题。我认为它值得一个单独的问题,最好是根据你的情况。说明您的数据来自哪里,您希望其中的内容是什么,您想要什么样的结果。

标签: go case-folding


【解决方案1】:

编辑:我的初始代码实际上仍然允许映射语法,因此允许绕过方法。这个版本更安全。

你可以“派生”一个类型。在 Go 中,我们只说声明。然后你在你的类型上定义方法。只需要一个非常薄的包装器即可提供您想要的功能。但请注意,您必须使用普通的方法调用语法调用 get 和 set。没有办法保留内置地图的索引语法或可选的 ok 结果。

package main

import (
    "fmt"
    "strings"
)

type ciMap struct {
    m map[string]bool
}

func newCiMap() ciMap {
    return ciMap{m: make(map[string]bool)}
}

func (m ciMap) set(s string, b bool) {
    m.m[strings.ToLower(s)] = b
}

func (m ciMap) get(s string) (b, ok bool) {
    b, ok = m.m[strings.ToLower(s)]
    return
}

func main() {
    m := newCiMap()
    m.set("key1", true)
    m.set("kEy1", false)
    k := "keY1"
    b, _ := m.get(k)
    fmt.Println(k, "value is", b)
}

【讨论】:

  • 映射到小写不适用于 Unicode 数据,仅适用于 ASCII。您应该在这里映射到 Unicode foldcase,而不是小写。否则,您的任务就是西西弗斯式的任务,因为Σίσυφος 的小写字母是σίσυφος,而其大写字母ΣΊΣΥΦΟΣ 的小写字母是正确的σίσυφοσ,这确实是所有这些的折叠。你现在明白为什么 Unicode 有一个单独的映射了吗?大小写映射过于复杂,无法盲目地映射到任何不是为明确目的而设计的东西,因此在 Unicode 大小写表中存在第四个 casemap:大写、标题大写、小写、折叠。
  • 要求是字符串。 Go 对字符串使用 Unicode,而不是 ASCII。他们要求提供不区分大小写的地图。你提供了一个纯 ASCII 的解决方案,晚上懒得提这个。我的 cmets 完全符合主题,因为您没有按要求和措辞回答问题,它没有仅限 ASCII 的限制。现在,事实证明这个人实际上只有 ASCII,所以即使在一般情况下它是错误的,你的解决方案也会偷偷摸摸。编写适用于 Unicode 的解决方案,它们也适用于 ASCII - 但反过来不成立,这就是你的代码有问题的原因。
  • 请注意,ToUpper 应该是首选 - 请参阅我的答案
【解决方案2】:

两种可能性:

  1. 如果您输入的转换为大写/小写,保证仅限于转换为大写/小写将产生正确结果的字符(对于某些 Unicode 字符可能不正确)

  2. 否则转换为 Unicode 折叠大小写:

使用 unicode.SimpleFold(rune) 将 unicode rune 转换为折叠大小写。显然,这比简单的 ASCII 风格的大小写映射要昂贵得多,但它也更易于移植到其他语言。请参阅the source code for EqualsFold 了解如何使用它,包括如何从源字符串中提取 Unicode 符文。

显然,您应该将此功能抽象到一个单独的包中,而不是在您使用地图的任何地方重新实现它。这应该是不言而喻的,但你永远不会知道。

【讨论】:

  • 但这很容易出错,因为它可能是作为库公开的,或者我可以忘记这样做。有没有办法创建一个可以自动完成的派生类型?
  • 反对票转换为赞成票。我对unicode.SimpleFold 仍然有点不确定,因为它似乎遍历了简单的折叠可能性,而不是像你想要的那样使用toSimpleFoldtoFullFold 字符串映射实际生成折叠映射。关于“tschüß”和“tschüss”的问题在于它不是特定于语言环境的东西。根据表格,这是任何语言的 casefold。另一方面,“tschuess”等价将是特定于语言环境的东西。实际上,这些是不同的问题。
【解决方案3】:

这里有比 strings.ToLower 更强大的东西,你可以使用 golang.org/x/text/cases 包。示例:

package main
import "golang.org/x/text/cases"

func main() {
   s := cases.Fold().String("March")
   println(s == "march")
}

如果你想使用标准库中的东西,我运行了这个测试:

package main

import (
   "strings"
   "unicode"
)

func main() {
   var (
      lower, upper int
      m = make(map[string]bool)
   )
   for n := '\u0080'; n <= '\u07FF'; n++ {
      q, r := n, n
      for {
         q = unicode.SimpleFold(q)
         if q == n { break }
         for {
            r = unicode.SimpleFold(r)
            if r == n { break }
            s, t := string(q), string(r)
            if m[t + s] { continue }
            if strings.ToLower(s) == strings.ToLower(t) { lower++ }
            if strings.ToUpper(s) == strings.ToUpper(t) { upper++ }
            m[s + t] = true
         }
      }
   }
   println(lower == 951, upper == 989)
}

因此可以看出,ToUpper 是稍微好一点的选择。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多