2

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.

hichris123
  • 10,145
  • 15
  • 56
  • 70
Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
  • Why is it duplicate? Those explicitly use NSString and NSData. I stated I was after a solution that used neither of those. – Travis Griggs Dec 04 '15 at 02:07

3 Answers3

5

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"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
2
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)
user3441734
  • 16,722
  • 2
  • 40
  • 59
1

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)
Travis Griggs
  • 21,522
  • 19
  • 91
  • 167
  • 1
    Here http://stackoverflow.com/a/24757284/1187415 is a generic string extension which builds a string from any UTF-8/16/32 sequence, e.g. `let s = String.init(codeUnits: utf8stream, codec: UTF8())`. – Martin R Dec 05 '15 at 20:22
  • thank you so much for such a great extension! Made a more generic version: `init(utf8stream: T) {` and this allowed me to use `ArraySlice` which was very helpful for me, I needed to parse a very long array of `UInt8`s to produce a lot of strings, so calling init with ArraySlice is much faster than allocating an array from this slice. – Sega-Zero May 01 '16 at 23:13