从Unicode字符串中获取随机表情符号/字符

4

我的目标是从一个列表中获取一个随机表情符号,使用F#编程语言。

我从以下代码开始:

let pickOne (icons: string) : char = icons.[Helpers.random.Next(icons.Length)]
let happySymbols = ""
let sadSymbols   = ""

这不起作用的原因是:

"".Length

当使用length函数计算字符串长度时,它会返回字符数,而这种方式无法正确处理Unicode字符。如果将字符串中添加一些单字节字符,就不能简单地将其除以2。

另外,索引也无法解决这个问题:

let a = ""
a.[0]

程序不会返回,但我收到了一些未知字符符号。

因此,计划B是:让我们将它变成一个数组而不是一个字符串:

let a = [| ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; ''; '' |]

这段代码无法编译,我得到了以下错误:

解析错误:绑定中出现意外的引号符。期望'|]'或其他令牌。

为什么会这样呢?

不管怎样,我可以制作一个字符串列表并使其正常工作,但我很好奇:是否有一种“正确”的方法可以让第一个代码段正常工作,并从Unicode字符串中获取随机的Unicode字符?


尝试将单引号替换为双引号。你会得到一个表情符号的字符串数组。 - FRocha
是的,这就是我在问题的最后一行写的内容;我知道我可以创建一个字符串数组,但我的问题是为什么使用字符不起作用。 - Thomas
如果你对第二个图标使用 icons.Substring(2, 2) ,它就能正常工作。 - Bent Tranberg
@BentTranberg 如果混合中有任何非表情符号字符,则无法正常工作。 - Asti
我知道。这就是为什么我不想基于此给出答案的原因。 - Bent Tranberg
2个回答

4
Asti的回答对你来说是有效的,但我对我们在这个问题上的处理结果感到不太满意。我想我在答案中卡在了“proper”这个词上。在各种地方进行了大量研究后,我对String.EnumerateRunes方法产生了好奇心,这又让我发现了Rune类型。该类型的文档特别揭示了正确的字符串处理方式以及在.NET中Unicode UTF-8字符串中的内容。我还在LINQPad中进行了实验,并得出了以下结果。
let dump x = x.Dump()
let runes = "abcABCæøåÆØÅ₅茨茧茦茥".EnumerateRunes().ToArray()
runes.Length |> dump
// 20
runes |> Array.iter (fun rune -> dump (string rune))
// a b c A B C æ ø å Æ Ø Å    ₅ 茨 茧 茦 茥
dump runes
// see screenshot
let smiley = runes.[13].ToString()
dump smiley
// 

enter image description here


我对Rune类型一无所知,但看起来这可能是一个好的选择,因为在我的实现中,需要知道每个符号的大小等信息。明天我会进行尝试! - Thomas
这是一个不错的发现!看起来是 .NET Core 3.0 的一个新功能。 - Asti

2

.NET中的所有字符串都是16位Unicode字符串。

这就是char的定义:

表示一个字符作为UTF-16代码单元。

所有字符占用最小编码大小(UTF-16的2个字节),直到需要多少个字节为止。表情符号不适合2个字节,因此它们会对齐到4个字节或2个字符。

那么解决方案是什么?把所有东西都对齐到4个字节!(在此插入GCC笑话)。

首先,我们将所有内容转换为UTF32

let utf32 (source: string) =
    Encoding.Convert(Encoding.Unicode, Encoding.UTF32, Encoding.Unicode.GetBytes(source))

然后我们可以挑选任何“字符”:

let pick (arr: byte[]) index = 
    Encoding.UTF32.GetString(arr, index * 4, 4)

测试:

let happySymbols = "YTHO"

pick (utf32 happySymbols) 0;;
val it : string = ""

> pick (utf32 happySymbols) 22;;
val it : string = "Y"

实际长度只需除以4即可。

let surpriseMe arr =
    let rnd = Random()
    pick arr (rnd.Next(0, arr.Length / 4))

嗯嗯。
> surpriseMe (utf32 happySymbols);;
val it : string = ""

2
嘿@Thomas,这是有关Unicode的好入门文章 - Asti

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接