I know questions like this exist on both Stack Overflow and elsewhere. But it seems to have evolved a lot as well.
Given a list of UInt8
(a swift byte array basically), what is the easiest/idiomatic way to covert it to a swift String
?
I'm particularly interested in the method that doesn't use NSData/NSString, since if Santa brings Swift to the world of Linux, it will undoubtedly be without the NS libraries, and I'd like to know how to do it in just Swift.
let buffUInt8: Array<UInt8> = [97, 98, 115, 100, 114, 102, 103, 104, 0]
// you need Int8 array
let buffInt8 = buffUInt8.map{ Int8(bitPattern: $0)}
let str = String.fromCString(buffInt8) // "absdrfgh"
alternatively you can use
String.fromCStringRepairingIllFormedUTF8(cs: UnsafePointer<CChar>) -> (String?, hadError: Bool)
Xcode 8 • Swift 3
extension Collection where Iterator.Element == UInt8 {
var bytes: [UInt8] { return Array(self) }
var data: Data { return Data(self) }
var string: String? { return String(data: data, encoding: .utf8) }
}
extension String {
var data: Data { return Data(utf8) }
}
usage:
let sentence = "Hello World"
let utf8View = sentence.utf8
let bytes = utf8View.bytes // [72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]
let data1 = sentence.data
print(data1 as NSData) // <48656c6c 6f20576f 726c64>
let data2 = utf8View.data
let data3 = bytes.data
let string1 = utf8View.string // "Hello World"
let string2 = bytes.string // "Hello World"
let string3 = data1.string // "Hello World"
I actually ended up needing to do this for a stream of UInt8
and was curious how hard utf8 decoding is. It's definitely not a one liner, but through the following direct implementation together:
import UIKit
let bytes:[UInt8] = [0xE2, 0x82, 0xEC, 0x00]
var g = bytes.generate()
extension String {
init(var utf8stream:IndexingGenerator<[UInt8]>) {
var result = ""
var codepoint:UInt32 = 0
while let byte = utf8stream.next() where byte != 0x00 {
codepoint = UInt32(byte)
var extraBytes = 0
if byte & 0b11100000 == 0b11000000 {
extraBytes = 1
codepoint &= 0b00011111
}
else if byte & 0b11110000 == 0b11100000 {
extraBytes = 2
codepoint &= 0b00001111
}
else if byte & 0b11111000 == 0b11110000 {
extraBytes = 3
codepoint &= 0b00000111
}
else if byte & 0b11111100 == 0b11111000 {
extraBytes = 4
codepoint &= 0b00000011
}
else if byte & 0b11111110 == 0b11111100 {
extraBytes = 5
codepoint &= 0b00000001
}
for _ in 0..<extraBytes {
if let additionalByte = utf8stream.next() {
codepoint <<= 6
codepoint |= UInt32(additionalByte & 0b00111111)
}
}
result.append(UnicodeScalar(codepoint))
}
self = result
}
}
String(utf8stream: g)