首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Go:如何找出符文的Unicode属性?

Go:如何找出符文的Unicode属性?
EN

Stack Overflow用户
提问于 2017-03-27 18:39:54
回答 2查看 306关注 0票数 4

我想找出一个符文的Unicode属性,特别是它的script属性的值。统一码有这样的说法(在http://www.unicode.org/reports/tr24/第1.5节):

代码语言:javascript
复制
The script property assigns a single value to each character, either
explicitly associating it with a particular script, or assigning one
of several specail [sic] values.

Go的unicode包为我提供了一种问“这个符文在脚本x中吗?”的方法,但是我没有办法问“这个符文在什么脚本中?”。我显然可以遍历所有的脚本,但那将是浪费的。有没有更聪明的方法来找出符文的脚本?(我总是可以实现一个自组织列表,但是我在标准go库中寻找一些我已经做了我想要的,但我忽略了的东西。)

谢谢大家!

EN

回答 2

Stack Overflow用户

发布于 2017-03-28 00:07:15

最简单、最快速的解决方案是编写函数。例如,

代码语言:javascript
复制
package main

import (
    "fmt"
    "unicode"
)

var runeScript map[rune]string

func init() {
    const nChar = 128172 // Version 9.0.0
    runeScript = make(map[rune]string, nChar*125/100)
    for s, rt := range unicode.Scripts {
        for _, r := range rt.R16 {
            for i := r.Lo; i <= r.Hi; i += r.Stride {
                runeScript[rune(i)] = s
            }
        }
        for _, r := range rt.R32 {
            for i := r.Lo; i <= r.Hi; i += r.Stride {
                runeScript[rune(i)] = s
            }
        }
    }
}

func script(r rune) string {
    return runeScript[r]
}

func main() {
    chars := []rune{' ', '0', 'a', 'α', 'А', 'ㄱ'}
    for _, c := range chars {
        s := script(c)
        fmt.Printf("%q %s\n", c, s)
    }
}

输出:

代码语言:javascript
复制
$ go run script.go
' ' Common
'0' Common
'a' Latin
'α' Greek
'А' Cyrillic
'ㄱ' Hangul
$ 
票数 5
EN

Stack Overflow用户

发布于 2017-03-28 07:04:52

改进PeterSO的答案

PeterSO的回答很好,很清楚。但是它在内存使用上并不容易,因为它在一个映射中存储了超过十万个条目,值是string类型的。即使一个string值只是一个存储指针和长度的头(参见reflect.StringHeader),在一个映射中拥有如此多的指针和长度仍然是多MB的(比如6MB)!

由于可能的不同string值(不同脚本名称)的数量很少(137个),我们可以选择使用值类型byte,它将只是存储实际脚本名称的切片中的一个索引。

下面是它可能的样子:

代码语言:javascript
复制
var runeScript map[rune]byte

var names = []string{""}

func init() {
    const nChar = 128172 // Version 9.0.0
    runeScript = make(map[rune]byte, nChar*125/100)
    for s, rt := range unicode.Scripts {
        idx := byte(len(names))
        names = append(names, s)
        for _, r := range rt.R16 {
            for i := r.Lo; i <= r.Hi; i += r.Stride {
                runeScript[rune(i)] = idx
            }
        }
        for _, r := range rt.R32 {
            for i := r.Lo; i <= r.Hi; i += r.Stride {
                runeScript[rune(i)] = idx
            }
        }
    }
}

func script(r rune) string {
    return names[runeScript[r]]
}

func main() {
    chars := []rune{' ', '0', 'a', 'α', 'А', 'ㄱ'}
    for _, c := range chars {
        s := script(c)
        fmt.Printf("%q %s\n", c, s)
    }
}

与使用map[rune]string相比,这种简单的改进只需要三分之一的内存。输出是相同的(在Go Playground上试试):

代码语言:javascript
复制
' ' Common
'0' Common
'a' Latin
'α' Greek
'А' Cyrillic
'ㄱ' Hangul

构建合并的范围切片

使用map[rune]byte将导致大约2MB的内存使用,并且需要“一些”时间来构建这个映射,这可能是可以接受的,也可能是不可接受的。

还有另一种方法/解决方案。我们可以选择不构建“所有”符文的映射,而只存储所有范围的切片(实际上是2个范围切片,一个具有16位Unicode值,另一个具有32位Unicode代码点)。

这样做的好处是,射程的数量远远少于符文的数量:只有852个(与100,000+符文相比)。与解决方案#1相比,总共具有852个元素的2个切片的存储器使用将是可以忽略的。

在我们的ranges中,我们还存储了脚本(name),因此我们可以返回此信息。我们也可以只存储一个名称索引(如解决方案#1所示),但是因为我们只有852个范围,所以不值得这样做。

我们将对范围切片进行排序,这样我们就可以在其中使用二进制搜索(一个切片中大约400个元素,二进制搜索:我们得到的结果最多7步,最坏的情况是在两种情况下都重复二进制搜索: 15步)。

好的,让我们来看看。我们使用这些范围封装器:

代码语言:javascript
复制
type myR16 struct {
    r16    unicode.Range16
    script string
}

type myR32 struct {
    r32    unicode.Range32
    script string
}

并将它们存储在:

代码语言:javascript
复制
var allR16 = []*myR16{}
var allR32 = []*myR32{}

我们像这样初始化/填充它们:

代码语言:javascript
复制
func init() {
    for script, rt := range unicode.Scripts {
        for _, r16 := range rt.R16 {
            allR16 = append(allR16, &myR16{r16, script})
        }
        for _, r32 := range rt.R32 {
            allR32 = append(allR32, &myR32{r32, script})
        }
    }

    // sort
    sort.Slice(allR16, func(i int, j int) bool {
        return allR16[i].r16.Lo < allR16[j].r16.Lo
    })
    sort.Slice(allR32, func(i int, j int) bool {
        return allR32[i].r32.Lo < allR32[j].r32.Lo
    })
}

最后,在排序的范围切片中进行搜索:

代码语言:javascript
复制
func script(r rune) string {
    // binary search over ranges
    if r <= 0xffff {
        r16 := uint16(r)
        i := sort.Search(len(allR16), func(i int) bool {
            return allR16[i].r16.Hi >= r16
        })

        if i < len(allR16) && allR16[i].r16.Lo <= r16 && r16 <= allR16[i].r16.Hi {
            return allR16[i].script
        }
    }

    r32 := uint32(r)
    i := sort.Search(len(allR32), func(i int) bool {
        return allR32[i].r32.Hi >= r32
    })

    if i < len(allR32) && allR32[i].r32.Lo <= r32 && r32 <= allR32[i].r32.Hi {
        return allR32[i].script
    }

    return ""
}

注意:在unicode包的所有脚本中,Stride始终为1,我利用了这一点(并且没有将其包括在算法中)。

使用相同的代码进行测试,我们会得到相同的输出。在Go Playground上试试。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43044164

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档