如何在 Swift 中解析抽搐 IRC 表情?

问题描述

我正在尝试解析 Twitch IRC 表情并从字符串中提取它。 Twitch IRC 消息提供了一个范围,它是字符串中表情的位置。示例:

string="Kappa abcdef";范围开始=0;范围结束=4

表示表情 Kappa 位于字符串“Kappa abcdef”中的索引 0 到 4 之间。

这是我的函数,它将根据范围返回一个表情:

func extractEmoteName(from message: String,rangeStart: Int,rangeEnd: Int) -> String? {
    guard
        let indexStart = message.index(message.startIndex,offsetBy: rangeStart,limitedBy: message.endindex),let limitIndex = message.index(message.endindex,offsetBy: -1,limitedBy: message.startIndex),let indexEnd = message.index(message.startIndex,offsetBy: rangeEnd,limitedBy: limitIndex)
    else {
        print("out of bounds")
        return nil
    }
    
    return String(message[indexStart ... indexEnd])
}

在示例字符串上使用它:extractEmoteName(from: "Kappa abcdef",rangeStart: 0,rangeEnd: 4) 将返回 Kappa 作为结果。

我在处理包含带有多个 unicode 标量的表情符号的字符串时遇到问题:

string="⚠️ Kappa";范围开始=3;范围结束=7

提取函数的结果是nil

extractEmoteName(from: "⚠️ Kappa",rangeStart: 3,rangeEnd: 7) // prints "out of bounds"

同时,带有单个标量表情的消息解析得很好,因为 Twitch 提供的范围不同:

string="? 河童";范围开始=2;范围结束=6

extractEmoteName(from: "? Kappa",rangeStart: 2,rangeEnd: 6) // returns "Kappa"

应该在 extractEmoteName 中进行哪些更改以使其适用于所有表情符号字符?

解决方法

您需要使用 unicodeScalars,它将以 unicode 标量表示字符串:

func extractEmoteName(from input: String,rangeStart: Int,rangeEnd: Int) -> String? {
    let message = input.unicodeScalars
    
    guard
        let indexStart = message.index(message.startIndex,offsetBy: rangeStart,limitedBy: message.endIndex),let limitIndex = message.index(message.endIndex,offsetBy: -1,limitedBy: message.startIndex),let indexEnd = message.index(message.startIndex,offsetBy: rangeEnd,limitedBy: limitIndex)
    else {
        print("out of bounds")
        return nil
    }
    
    return String(message[indexStart ... indexEnd])
,

您可以通过计算标量来实现。

func extractEmoteName(from message: String,rangeEnd: Int) -> String? {
    guard rangeStart >= 0 && rangeStart <= rangeEnd else {
        return nil
    }
    let scalars = message.unicodeScalars
    let start = scalars.index(scalars.startIndex,offsetBy: rangeStart)
    let end = scalars.index(scalars.startIndex,offsetBy: rangeEnd)
    let nameScalars = scalars[start...end]
    return String(nameScalars)
}