Convert UInt32 (UTF-32) to String in Swift
Asked Answered
S

2

3

I have an array of UInt32 values. I would like to convert this array to a String.

This doesn't work:

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049]
let myString = String(myUInt32Array) // error
let myString = String(stringInterpolationSegment: myUInt32Array) // [72, 101, 108, 108, 111, 128049] (not what I want)

These SO posts show UTF8 and UTF16:

Saltandpepper answered 15/7, 2015 at 3:34 Comment(0)
S
4

UnicodeScalar is a type alias for UInt32. So cast your UInt32 values to UnicodeScalar and then append them to a String.

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049]

var myString: String = ""

for value in myUInt32Array {
    if let scalar = UnicodeScalar(value) {
        myString.append(Character(scalar))
    }
}

print(myString) // Hello🐱
Saltandpepper answered 15/7, 2015 at 3:34 Comment(1)
@jvarela, updated. Thanks for the notice. In addition to converting to Character first, the UInt32 value must also be checked for nil.Saltandpepper
R
3

(The answer has been updated for Swift 4 and later.)

Using the Swift type Data and String this can be done as

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049, 127465, 127466]
let data = Data(bytes: myUInt32Array, count: myUInt32Array.count * MemoryLayout<UInt32>.stride)
let myString = String(data: data, encoding: .utf32LittleEndian)!
print(myString) // Hello🐱🇩🇪

A forced unwrap is used here because a conversion from UTF-32 code points to a string cannot fail.

You can define a String extension for your convenience

extension String {
    init(utf32chars:[UInt32]) {
        let data = Data(bytes: utf32chars, count: utf32chars.count * MemoryLayout<UInt32>.stride)
        self = String(data: data, encoding: .utf32LittleEndian)!
    }
}

and use it as

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049, 127465, 127466]
let myString = String(utf32chars: myUInt32Array)
print(myString) // Hello🐱🇩🇪

And just for completeness, the generic converter from https://mcmap.net/q/23389/-is-there-a-way-to-create-a-string-from-utf16-array-in-swift

extension String {
    init?<C : UnicodeCodec>(codeUnits:[C.CodeUnit], codec : C) {
        var codec = codec
        var str = ""
        var generator = codeUnits.makeIterator()
        var done = false
        while !done {
            let r = codec.decode(&generator)
            switch (r) {
            case .emptyInput:
                done = true
            case .scalarValue(let val):
                str.unicodeScalars.append(val)
            case .error:
                return nil
            }
        }
        self = str
    }
}

can be used with UTF-8, UTF-16 and UTF-32 input. In your case it would be

let myUInt32Array: [UInt32] = [72, 101, 108, 108, 111, 128049, 127465, 127466]
let myString = String(codeUnits: myUInt32Array, codec : UTF32())!
print(myString) // Hello🐱🇩🇪
Runck answered 15/7, 2015 at 7:13 Comment(4)
Thank you for your answer. I have a few followup questions. (1) When should I choose to use the Foundation classes as opposed to the answer I gave? (2) I've read and understand a little about big endian and little endian byte order. But what is the reasoning behind using NSUTF32LittleEndianStringEncoding in your answer? Do I need to change anything when saving strings to a file or sending them over the network? If I just use the Swift classes can I avoid worrying about big/little endian encoding?Saltandpepper
@Suragch: 1) I don't think there is a clear advantage of one over the other. I have shown the NSData/NSString method as an alternative because it is a little less code, but your method works as well. (That's also why I find your bounty question difficult to answer: All those methods work, so you can choose what you feel most familiar with.)Runck
@Suragch: 2) All current OS X and iOS devices use little-endian for storing integer numbers in memory, therefore NSUTF32LittleEndianStringEncoding. Of course, if you sent data over the network to some different device with a possibly different byte order then you have to take care of that. But that is valid generally, and unrelated to your current question.Runck
@l--marcl: I have updated the code for the current Swift.Runck

© 2022 - 2024 — McMap. All rights reserved.