Swift 2.1 [UInt8] --utf8--> String?

2020-01-30 01:54发布

问题:

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.

回答1:

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)


回答2:

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"


回答3:

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)