如何迭代 Crystal 中的字素簇?

问题描述

Unicode 标准将 grapheme cluster 定义为“用户感知字符”的算法近似。字素簇或多或少对应于人们认为的文本中的单个“字符”。因此,能够将字符串作为字素簇序列进行操作是编程中一个自然而重要的要求。

最好的通用字形簇定义是扩展字形簇;还有其他字素簇算法(量身定制的字素簇)用于特定的本地化用途。

在 Crystal 中,如何迭代(或以其他方式操作)String 作为一系列字形簇?

解决方法

此答案基于 Crystal forum 中的一个线程。

从 1.0.0 开始,Crystal 没有内置的方法来做到这一点(不幸的是)。

但是,Crystal 中的 regex 引擎可以,使用 \X 模式匹配单个扩展字素集群:

"\u0067\u0308\u1100\u1161\u11A8".scan(/\X/) do |match|
  grapheme = match[0]
  puts grapheme
end

# Output:
# g̈
# 각

Run it online

您可以将其封装在更好的 API 中,如下所示:

def each_grapheme(s : String,&)
  s.scan(/\X/) do |match|
    yield match[0]
  end
end

def graphemes(s : String) : Array(String)
  result = Array(String).new
  each_grapheme(s) do |g|
    result << g
  end
  return result
end

# Example from https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html
s = "\u{E9}\u{65}\u{301}\u{D55C}\u{1112}\u{1161}\u{11AB}"
each_grapheme(s) do |g|
  puts "#{g}\t#{g.codepoints}"
end

# Output:
# é [233]
# é    [101,769]
# 한 [54620]
# 한   [4370,4449,4523]

Run it online