为了账号安全,请及时绑定邮箱和手机立即绑定

在Swift中使用Unicode代码点

在Swift中使用Unicode代码点

慕斯709654 2019-11-30 15:14:02
如果您对蒙古语的细节不感兴趣,而只是想快速了解在Swift中使用和转换Unicode值,那么请跳至已接受答案的第一部分。背景我要为iOS应用程序中使用的传统蒙古语呈现Unicode文本。更好的长期解决方案是使用AAT智能字体来呈现此复杂脚本。(确实存在这样的字体,但是它们的许可证不允许修改和非个人使用。)但是,由于我从未制作过字体,更不用说AAT字体的所有渲染逻辑了,我只打算自己进行渲染。斯威夫特暂时。也许以后我可以学习制作智能字体。在外部,我将使用Unicode文本,但在内部(以显示为UITextView),我将Unicode转换为以哑字体(用Unicode PUA值编码)存储的单个字形。因此,我的渲染引擎需要将蒙古Unicode值(范围:U + 1820到U + 1842)转换为存储在PUA中的字形值(范围:U + E360到U + E5CF)。无论如何,这是我的计划,因为这是我过去在Java中所做的,但是也许我需要改变整体思维方式。例下图显示了使用两种不同形式的字母u(用红色)在蒙古语中写过su两次。(蒙古语是垂直书写的,字母像草书字母一样用英语连接。)在此处输入图片说明在Unicode中,这两个字符串将表示为var suForm1: String = "\u{1830}\u{1826}"var suForm2: String = "\u{1830}\u{1826}\u{180B}"suForm2Swift 将自由变体选择器(U + 180B)识别(正确)为在其前面String带有u(U + 1826)的单元。Swift将其视为单个字符,扩展的字素簇。但是,出于自己进行渲染的目的,我需要将u(U + 1826)和FVS1(U + 180B)区分为两个不同的UTF-16代码点。为了内部显示,我将上述Unicode字符串转换为以下呈现的字形字符串:suForm1 = "\u{E46F}\u{E3BA}" suForm2 = "\u{E46F}\u{E3BB}"题我一直在和Swift String和Character。关于它们有很多方便的事情,但是由于在我的特殊情况下,我只处理UTF-16代码单元,所以我想知道是否应该使用旧的NSString而不是Swift的代码String。我意识到我可以String.utf16用来获取UTF-16代码点,但是转换回String并不是很好。坚持使用String和Character还是应该使用NSString和更好unichar?
查看完整描述

2 回答

?
素胚勾勒不出你

TA贡献1827条经验 获得超9个赞

为Swift 3更新


字符串和字符

对于几乎每个人都在谁访问了这个问题,未来String,并Character会为你的答案。


直接在代码中设置Unicode值:


var str: String = "I want to visit 北京, Москва, मुंबई, القاهرة, and 서울시. ?"

var character: Character = "?"

使用十六进制设置值


var str: String = "\u{61}\u{5927}\u{1F34E}\u{3C0}" // a大?π

var character: Character = "\u{65}\u{301}" // é = "e" + accent mark

请注意,Swift字符可以由多个Unicode代码点组成,但看起来像是一个字符。这称为扩展字素簇。


另请参阅此问题。


转换为Unicode值:


str.utf8

str.utf16

str.unicodeScalars // UTF-32


String(character).utf8

String(character).utf16

String(character).unicodeScalars

从Unicode十六进制值转换:


let hexValue: UInt32 = 0x1F34E


// convert hex value to UnicodeScalar

guard let scalarValue = UnicodeScalar(hexValue) else {

    // early exit if hex does not form a valid unicode value

    return

}


// convert UnicodeScalar to String

let myString = String(scalarValue) // ?

或者:


let hexValue: UInt32 = 0x1F34E

if let scalarValue = UnicodeScalar(hexValue) {

    let myString = String(scalarValue)

}

其他一些例子


let value0: UInt8 = 0x61

let value1: UInt16 = 0x5927

let value2: UInt32 = 0x1F34E


let string0 = String(UnicodeScalar(value0)) // a

let string1 = String(UnicodeScalar(value1)) // 大

let string2 = String(UnicodeScalar(value2)) // ?


// convert hex array to String

let myHexArray = [0x43, 0x61, 0x74, 0x203C, 0x1F431] // an Int array

var myString = ""

for hexValue in myHexArray {

    myString.append(UnicodeScalar(hexValue))

}

print(myString) // Cat‼?

请注意,对于UTF-8和UTF-16,转换并不总是那么容易。(请参阅UTF-8,UTF-16和UTF-32问题。)


NSString和unichar

也可能与工作NSString和unichar斯威夫特,但你应该明白,除非你是熟悉的语法转换为斯威夫特目标C和好,这将是很难找到良好的文档。


同样,unichar是一个UInt16数组,如上所述,从UInt16Unicode标量值的转换并不总是那么容易(即,将诸如emoji表情和其他字符的对象的替代对在上层代码平面中转换)。


自定义字符串结构

由于问题中提到的原因,我最终没有使用任何上述方法。相反,我编写了自己的字符串结构,该结构基本上是一个数组,UInt32用于保存Unicode标量值。


同样,这不是大多数人的解决方案。如果只需要扩展或扩展一点功能,请首先考虑使用扩展。StringCharacter


但是,如果确实需要专门处理Unicode标量值,则可以编写自定义结构。


优点是:


不需要类型(间不断切换String,Character,UnicodeScalar,UInt32做字符串操作时,等)。

完成Unicode操作后,最终的转换String很容易。

易于在需要时添加更多方法

简化了从Java或其他语言的代码转换

缺点是:


使代码的可移植性和其他Swift开发人员的可读性降低

没有像本机Swift类型那样经过充分测试和优化

它是您每次需要时都必须包含在项目中的另一个文件

您可以自己制作,但这是我的参考。最难的部分是使其变得可哈希化。


// This struct is an array of UInt32 to hold Unicode scalar values

// Version 3.4.0 (Swift 3 update)



struct ScalarString: Sequence, Hashable, CustomStringConvertible {


    fileprivate var scalarArray: [UInt32] = []



    init() {

        // does anything need to go here?

    }


    init(_ character: UInt32) {

        self.scalarArray.append(character)

    }


    init(_ charArray: [UInt32]) {

        for c in charArray {

            self.scalarArray.append(c)

        }

    }


    init(_ string: String) {


        for s in string.unicodeScalars {

            self.scalarArray.append(s.value)

        }

    }


    // Generator in order to conform to SequenceType protocol

    // (to allow users to iterate as in `for myScalarValue in myScalarString` { ... })

    func makeIterator() -> AnyIterator<UInt32> {

        return AnyIterator(scalarArray.makeIterator())

    }


    // append

    mutating func append(_ scalar: UInt32) {

        self.scalarArray.append(scalar)

    }


    mutating func append(_ scalarString: ScalarString) {

        for scalar in scalarString {

            self.scalarArray.append(scalar)

        }

    }


    mutating func append(_ string: String) {

        for s in string.unicodeScalars {

            self.scalarArray.append(s.value)

        }

    }


    // charAt

    func charAt(_ index: Int) -> UInt32 {

        return self.scalarArray[index]

    }


    // clear

    mutating func clear() {

        self.scalarArray.removeAll(keepingCapacity: true)

    }


    // contains

    func contains(_ character: UInt32) -> Bool {

        for scalar in self.scalarArray {

            if scalar == character {

                return true

            }

        }

        return false

    }


    // description (to implement Printable protocol)

    var description: String {

        return self.toString()

    }


    // endsWith

    func endsWith() -> UInt32? {

        return self.scalarArray.last

    }


    // indexOf

    // returns first index of scalar string match

    func indexOf(_ string: ScalarString) -> Int? {


        if scalarArray.count < string.length {

            return nil

        }


        for i in 0...(scalarArray.count - string.length) {


            for j in 0..<string.length {


                if string.charAt(j) != scalarArray[i + j] {

                    break // substring mismatch

                }

                if j == string.length - 1 {

                    return i

                }

            }

        }


        return nil

    }


    // insert

    mutating func insert(_ scalar: UInt32, atIndex index: Int) {

        self.scalarArray.insert(scalar, at: index)

    }

    mutating func insert(_ string: ScalarString, atIndex index: Int) {

        var newIndex = index

        for scalar in string {

            self.scalarArray.insert(scalar, at: newIndex)

            newIndex += 1

        }

    }

    mutating func insert(_ string: String, atIndex index: Int) {

        var newIndex = index

        for scalar in string.unicodeScalars {

            self.scalarArray.insert(scalar.value, at: newIndex)

            newIndex += 1

        }

    }


    // isEmpty

    var isEmpty: Bool {

        return self.scalarArray.count == 0

    }


    // hashValue (to implement Hashable protocol)

    var hashValue: Int {


        // DJB Hash Function

        return self.scalarArray.reduce(5381) {

            ($0 << 5) &+ $0 &+ Int($1)

        }

    }


    // length

    var length: Int {

        return self.scalarArray.count

    }


    // remove character

    mutating func removeCharAt(_ index: Int) {

        self.scalarArray.remove(at: index)

    }

    func removingAllInstancesOfChar(_ character: UInt32) -> ScalarString {


        var returnString = ScalarString()


        for scalar in self.scalarArray {

            if scalar != character {

                returnString.append(scalar)

            }

        }


        return returnString

    }

    func removeRange(_ range: CountableRange<Int>) -> ScalarString? {


        if range.lowerBound < 0 || range.upperBound > scalarArray.count {

            return nil

        }


        var returnString = ScalarString()


        for i in 0..<scalarArray.count {

            if i < range.lowerBound || i >= range.upperBound {

                returnString.append(scalarArray[i])

            }

        }


        return returnString

    }



    // replace

    func replace(_ character: UInt32, withChar replacementChar: UInt32) -> ScalarString {


        var returnString = ScalarString()


        for scalar in self.scalarArray {

            if scalar == character {

                returnString.append(replacementChar)

            } else {

                returnString.append(scalar)

            }

        }

        return returnString

    }

    func replace(_ character: UInt32, withString replacementString: String) -> ScalarString {


        var returnString = ScalarString()


        for scalar in self.scalarArray {

            if scalar == character {

                returnString.append(replacementString)

            } else {

                returnString.append(scalar)

            }

        }

        return returnString

    }

    func replaceRange(_ range: CountableRange<Int>, withString replacementString: ScalarString) -> ScalarString {


        var returnString = ScalarString()


        for i in 0..<scalarArray.count {

            if i < range.lowerBound || i >= range.upperBound {

                returnString.append(scalarArray[i])

            } else if i == range.lowerBound {

                returnString.append(replacementString)

            }

        }

        return returnString

    }


    // set (an alternative to myScalarString = "some string")

    mutating func set(_ string: String) {

        self.scalarArray.removeAll(keepingCapacity: false)

        for s in string.unicodeScalars {

            self.scalarArray.append(s.value)

        }

    }


    // split

    func split(atChar splitChar: UInt32) -> [ScalarString] {

        var partsArray: [ScalarString] = []

        if self.scalarArray.count == 0 {

            return partsArray

        }

        var part: ScalarString = ScalarString()

        for scalar in self.scalarArray {

            if scalar == splitChar {

                partsArray.append(part)

                part = ScalarString()

            } else {

                part.append(scalar)

            }

        }

        partsArray.append(part)

        return partsArray

    }


    // startsWith

    func startsWith() -> UInt32? {

        return self.scalarArray.first

    }


    // substring

    func substring(_ startIndex: Int) -> ScalarString {

        // from startIndex to end of string

        var subArray: ScalarString = ScalarString()

        for i in startIndex..<self.length {

            subArray.append(self.scalarArray[i])

        }

        return subArray

    }

    func substring(_ startIndex: Int, _ endIndex: Int) -> ScalarString {

        // (startIndex is inclusive, endIndex is exclusive)

        var subArray: ScalarString = ScalarString()

        for i in startIndex..<endIndex {

            subArray.append(self.scalarArray[i])

        }

        return subArray

    }


    // toString

    func toString() -> String {

        var string: String = ""


        for scalar in self.scalarArray {

            if let validScalor = UnicodeScalar(scalar) {

                string.append(Character(validScalor))

            }

        }

        return string

    }


    // trim

    // removes leading and trailing whitespace (space, tab, newline)

    func trim() -> ScalarString {


        //var returnString = ScalarString()

        let space: UInt32 = 0x00000020

        let tab: UInt32 = 0x00000009

        let newline: UInt32 = 0x0000000A


        var startIndex = self.scalarArray.count

        var endIndex = 0


        // leading whitespace

        for i in 0..<self.scalarArray.count {

            if self.scalarArray[i] != space &&

                self.scalarArray[i] != tab &&

                self.scalarArray[i] != newline {


                startIndex = i

                break

            }

        }


        // trailing whitespace

        for i in stride(from: (self.scalarArray.count - 1), through: 0, by: -1) {

            if self.scalarArray[i] != space &&

                self.scalarArray[i] != tab &&

                self.scalarArray[i] != newline {


                endIndex = i + 1

                break

            }

        }


        if endIndex <= startIndex {

            return ScalarString()

        }


        return self.substring(startIndex, endIndex)

    }


    // values

    func values() -> [UInt32] {

        return self.scalarArray

    }


}


func ==(left: ScalarString, right: ScalarString) -> Bool {

    return left.scalarArray == right.scalarArray

}


func +(left: ScalarString, right: ScalarString) -> ScalarString {

    var returnString = ScalarString()

    for scalar in left.values() {

        returnString.append(scalar)

    }

    for scalar in right.values() {

        returnString.append(scalar)

    }

    return returnString

}


查看完整回答
反对 回复 2019-11-30
?
HUWWW

TA贡献1874条经验 获得超12个赞

//Swift 3.0  

// This struct is an array of UInt32 to hold Unicode scalar values

struct ScalarString: Sequence, Hashable, CustomStringConvertible {


    private var scalarArray: [UInt32] = []


    init() {

        // does anything need to go here?

    }


    init(_ character: UInt32) {

        self.scalarArray.append(character)

    }


    init(_ charArray: [UInt32]) {

        for c in charArray {

            self.scalarArray.append(c)

        }

    }


    init(_ string: String) {


        for s in string.unicodeScalars {

            self.scalarArray.append(s.value)

        }

    }


    // Generator in order to conform to SequenceType protocol

    // (to allow users to iterate as in `for myScalarValue in myScalarString` { ... })


    //func generate() -> AnyIterator<UInt32> {

    func makeIterator() -> AnyIterator<UInt32> {


        let nextIndex = 0


        return AnyIterator {

            if (nextIndex > self.scalarArray.count-1) {

                return nil

            }

            return self.scalarArray[nextIndex + 1]

        }

    }


    // append

    mutating func append(scalar: UInt32) {

        self.scalarArray.append(scalar)

    }


    mutating func append(scalarString: ScalarString) {

        for scalar in scalarString {

            self.scalarArray.append(scalar)

        }

    }


    mutating func append(string: String) {

        for s in string.unicodeScalars {

            self.scalarArray.append(s.value)

        }

    }


    // charAt

    func charAt(index: Int) -> UInt32 {

        return self.scalarArray[index]

    }


    // clear

    mutating func clear() {

        self.scalarArray.removeAll(keepingCapacity: true)

    }


    // contains

    func contains(character: UInt32) -> Bool {

        for scalar in self.scalarArray {

            if scalar == character {

                return true

            }

        }

        return false

    }


    // description (to implement Printable protocol)

    var description: String {


        var string: String = ""


        for scalar in scalarArray {

            string.append(String(describing: UnicodeScalar(scalar))) //.append(UnicodeScalar(scalar)!)

        }

        return string

    }


    // endsWith

    func endsWith() -> UInt32? {

        return self.scalarArray.last

    }


    // insert

    mutating func insert(scalar: UInt32, atIndex index: Int) {

        self.scalarArray.insert(scalar, at: index)

    }


    // isEmpty

    var isEmpty: Bool {

        get {

            return self.scalarArray.count == 0

        }

    }


    // hashValue (to implement Hashable protocol)

    var hashValue: Int {

        get {


            // DJB Hash Function

            var hash = 5381


            for i in 0 ..< scalarArray.count {

                hash = ((hash << 5) &+ hash) &+ Int(self.scalarArray[i])

            }

            /*

             for i in 0..< self.scalarArray.count {

             hash = ((hash << 5) &+ hash) &+ Int(self.scalarArray[i])

             }

             */

            return hash

        }

    }


    // length

    var length: Int {

        get {

            return self.scalarArray.count

        }

    }


    // remove character

    mutating func removeCharAt(index: Int) {

        self.scalarArray.remove(at: index)

    }

    func removingAllInstancesOfChar(character: UInt32) -> ScalarString {


        var returnString = ScalarString()


        for scalar in self.scalarArray {

            if scalar != character {

                returnString.append(scalar: scalar) //.append(scalar)

            }

        }


        return returnString

    }


    // replace

    func replace(character: UInt32, withChar replacementChar: UInt32) -> ScalarString {


        var returnString = ScalarString()


        for scalar in self.scalarArray {

            if scalar == character {

                returnString.append(scalar: replacementChar) //.append(replacementChar)

            } else {

                returnString.append(scalar: scalar) //.append(scalar)

            }

        }

        return returnString

    }


    // func replace(character: UInt32, withString replacementString: String) -> ScalarString {

    func replace(character: UInt32, withString replacementString: ScalarString) -> ScalarString {


        var returnString = ScalarString()


        for scalar in self.scalarArray {

            if scalar == character {

                returnString.append(scalarString: replacementString) //.append(replacementString)

            } else {

                returnString.append(scalar: scalar) //.append(scalar)

            }

        }

        return returnString

    }


    // set (an alternative to myScalarString = "some string")

    mutating func set(string: String) {

        self.scalarArray.removeAll(keepingCapacity: false)

        for s in string.unicodeScalars {

            self.scalarArray.append(s.value)

        }

    }


    // split

    func split(atChar splitChar: UInt32) -> [ScalarString] {

        var partsArray: [ScalarString] = []

        var part: ScalarString = ScalarString()

        for scalar in self.scalarArray {

            if scalar == splitChar {

                partsArray.append(part)

                part = ScalarString()

            } else {

                part.append(scalar: scalar) //.append(scalar)

            }

        }

        partsArray.append(part)

        return partsArray

    }


    // startsWith

    func startsWith() -> UInt32? {

        return self.scalarArray.first

    }


    // substring

    func substring(startIndex: Int) -> ScalarString {

        // from startIndex to end of string

        var subArray: ScalarString = ScalarString()

        for i in startIndex ..< self.length {

            subArray.append(scalar: self.scalarArray[i]) //.append(self.scalarArray[i])

        }

        return subArray

    }

    func substring(startIndex: Int, _ endIndex: Int) -> ScalarString {

        // (startIndex is inclusive, endIndex is exclusive)

        var subArray: ScalarString = ScalarString()

        for i in startIndex ..< endIndex {

            subArray.append(scalar: self.scalarArray[i]) //.append(self.scalarArray[i])

        }

        return subArray

    }


    // toString

    func toString() -> String {

        let string: String = ""


        for scalar in self.scalarArray {

            string.appending(String(describing:UnicodeScalar(scalar))) //.append(UnicodeScalar(scalar)!)

        }

        return string

    }


    // values

    func values() -> [UInt32] {

        return self.scalarArray

    }


}


func ==(left: ScalarString, right: ScalarString) -> Bool {


    if left.length != right.length {

        return false

    }


    for i in 0 ..< left.length {

        if left.charAt(index: i) != right.charAt(index: i) {

            return false

        }

    }


    return true

}


func +(left: ScalarString, right: ScalarString) -> ScalarString {

    var returnString = ScalarString()

    for scalar in left.values() {

        returnString.append(scalar: scalar) //.append(scalar)

    }

    for scalar in right.values() {

        returnString.append(scalar: scalar) //.append(scalar)

    }

    return returnString

}


查看完整回答
反对 回复 2019-11-30
  • 2 回答
  • 0 关注
  • 729 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信