Как создать строку из UTF8 в Swift?

Мы знаем, что мы можем печатать каждый символ в кодовых единицах UTF8? Затем, если у нас есть единицы кода этих символов, как мы можем создать String с ними?

Ответ 1

Можно конвертировать кодовые точки UTF8 в строку Swift идиоматически с помощью класса UTF8 Swift. Хотя гораздо проще конвертировать из String в UTF8!

import Foundation

public class UTF8Encoding {
  public static func encode(bytes: Array<UInt8>) -> String {
    var encodedString = ""
    var decoder = UTF8()
    var generator = bytes.generate()
    var finished: Bool = false
    do {
      let decodingResult = decoder.decode(&generator)
      switch decodingResult {
      case .Result(let char):
        encodedString.append(char)
      case .EmptyInput:
        finished = true
      /* ignore errors and unexpected values */
      case .Error:
        finished = true
      default:
        finished = true
      }
    } while (!finished)
    return encodedString
  }

  public static func decode(str: String) -> Array<UInt8> {
    var decodedBytes = Array<UInt8>()
    for b in str.utf8 {
      decodedBytes.append(b)
    }
    return decodedBytes
  }
}

func testUTF8Encoding() {
  let testString = "A UTF8 String With Special Characters: 😀🍎"
  let decodedArray = UTF8Encoding.decode(testString)
  let encodedString = UTF8Encoding.encode(decodedArray)
  XCTAssert(encodedString == testString, "UTF8Encoding is lossless: \(encodedString) != \(testString)")
}

Из других предложенных альтернатив:

  • Использование NSString вызывает мост Objective-C;

  • Использование UnicodeScalar является подверженным ошибкам, поскольку оно преобразует UnicodeScalars непосредственно в символы, игнорируя сложные кластеры графемы; и

  • Использование String.fromCString потенциально опасно, поскольку оно использует указатели.

Ответ 2

улучшить ответ Мартина R

import AppKit

let utf8 : CChar[] = [65, 66, 67, 0]
let str = NSString(bytes: utf8, length: utf8.count, encoding: NSUTF8StringEncoding)
println(str) // Output: ABC

import AppKit

let utf8 : UInt8[] = [0xE2, 0x82, 0xAC, 0]
let str = NSString(bytes: utf8, length: utf8.count, encoding: NSUTF8StringEncoding)
println(str) // Output: €

Что произошло, Array может быть автоматически преобразован в CConstVoidPointer, который может использоваться для создания строки с NSSString(bytes: CConstVoidPointer, length len: Int, encoding: Uint)

Ответ 3

Я искал исчерпывающий ответ о строковых манипуляциях в Swift. Опираясь на приведение к и от NSString и другую небезопасную магию указаний, я просто не делал этого для меня. Здесь безопасная альтернатива:

Во-первых, мы хотим расширить UInt8. Это примитивный тип за CodeUnit.

extension UInt8 {
    var character: Character {
        return Character(UnicodeScalar(self))
    }
}

Это позволит нам сделать что-то вроде этого:

let codeUnits: [UInt8] = [
    72, 69, 76, 76, 79
]

let characters = codeUnits.map { $0.character }
let string     = String(characters)

// string prints "HELLO"

Оборудовавшись этим расширением, мы можем теперь изменять строки.

let string = "ABCDEFGHIJKLMONP"

var modifiedCharacters = [Character]()
for (index, utf8unit) in string.utf8.enumerate() {

    // Insert a "-" every 4 characters
    if index > 0 && index % 4 == 0 {
        let separator: UInt8 = 45 // "-" in ASCII
        modifiedCharacters.append(separator.character)
    }
    modifiedCharacters.append(utf8unit.character)
}

let modifiedString = String(modifiedCharacters)

// modified string == "ABCD-EFGH-IJKL-MONP"

Ответ 4

Swift 3

let s = String(bytes: arr, encoding: .utf8)

Ответ 5

Это возможное решение (теперь обновлено для Swift 2):

let utf8 : [CChar] = [65, 66, 67, 0]
if let str = utf8.withUnsafeBufferPointer( { String.fromCString($0.baseAddress) }) {
    print(str) // Output: ABC
} else {
    print("Not a valid UTF-8 string") 
}

Внутри замыкания $0 есть UnsafeBufferPointer<CChar>, указывающий на непрерывное хранилище массива. Из этого можно создать Swift String.

В качестве альтернативы, если вы предпочитаете ввод как неподписанные байты:

let utf8 : [UInt8] = [0xE2, 0x82, 0xAC, 0]
if let str = utf8.withUnsafeBufferPointer( { String.fromCString(UnsafePointer($0.baseAddress)) }) {
    print(str) // Output: €
} else {
    print("Not a valid UTF-8 string")
}

Ответ 6

Я бы сделал что-то вроде этого, он может быть не таким изящным, как работа с "указателями", но он хорошо выполняет эту работу, это в значительной степени связано с рядом новых операторов += для String, таких как:

@infix func += (inout lhs: String, rhs: (unit1: UInt8)) {
    lhs += Character(UnicodeScalar(UInt32(rhs.unit1)))
}

@infix func += (inout lhs: String, rhs: (unit1: UInt8, unit2: UInt8)) {
    lhs += Character(UnicodeScalar(UInt32(rhs.unit1) << 8 | UInt32(rhs.unit2)))
}

@infix func += (inout lhs: String, rhs: (unit1: UInt8, unit2: UInt8, unit3: UInt8, unit4: UInt8)) {
    lhs += Character(UnicodeScalar(UInt32(rhs.unit1) << 24 | UInt32(rhs.unit2) << 16 | UInt32(rhs.unit3) << 8 | UInt32(rhs.unit4)))
}

ПРИМЕЧАНИЕ. Вы можете расширить список поддерживаемых операторов с помощью переопределения оператора +, определяя список полностью коммутативных операторов для String.


и теперь вы можете добавить String с символом unicode (UTF-8, UTF-16 или UTF-32), например, например:

var string: String = "signs of the Zodiac: "
string += (0x0, 0x0, 0x26, 0x4b)
string += (38)
string += (0x26, 76)

Ответ 7

Если вы начинаете с необработанного буфера, например, из объекта Data, возвращаемого из дескриптора файла (в этом случае, взятого из объекта Pipe):

let data = pipe.fileHandleForReading.readDataToEndOfFile()
var unsafePointer = UnsafeMutablePointer<UInt8>.allocate(capacity: data.count)

data.copyBytes(to: unsafePointer, count: data.count)

let output = String(cString: unsafePointer)

Ответ 8

// Swift4
var units = [UTF8.CodeUnit]()
//
// update units
//
let str = String(decoding: units, as: UTF8.self)

Ответ 9

Есть версия Swift 3.0 Martin R answer

public class UTF8Encoding {
  public static func encode(bytes: Array<UInt8>) -> String {
    var encodedString = ""
    var decoder = UTF8()
    var generator = bytes.makeIterator()
    var finished: Bool = false
    repeat {
      let decodingResult = decoder.decode(&generator)
      switch decodingResult {
      case .scalarValue(let char):
        encodedString += "\(char)"
      case .emptyInput:
        finished = true
      case .error:
        finished = true
      }
    } while (!finished)
    return encodedString
  }
  public static func decode(str: String) -> Array<UInt8> {
    var decodedBytes = Array<UInt8>()
    for b in str.utf8 {
      decodedBytes.append(b)
    }
    return decodedBytes
  }
}

Если вы хотите показать emoji из строки UTF-8, просто используйте метод convertEmojiCodesToString для пользователей ниже. Он работает правильно для строк типа "U + 1F52B" (emoji) или "U + 1F1E6 U + 1F1F1" (флаг страны флагов)

class EmojiConverter {
  static func convertEmojiCodesToString(_ emojiCodesString: String) -> String {
    let emojies = emojiCodesString.components(separatedBy: " ")
    var resultString = ""
    for emoji in emojies {
      var formattedCode = emoji
      formattedCode.slice(from: 2, to: emoji.length)
      formattedCode = formattedCode.lowercased()
      if let charCode = UInt32(formattedCode, radix: 16),
        let unicode = UnicodeScalar(charCode) {
        let str = String(unicode)
        resultString += "\(str)"
      }
    }
    return resultString
  }
}

Ответ 10

Это расширение ответа xohozu (Swift 4.2):

let someString = "This is a String ç´´Ω"
var utf8String = ""

print("Initial string: \(someString)")

var units = [UTF8.CodeUnit]()
units.reserveCapacity(someString.count * 4)
for codeUnit in someString.utf8 {
    utf8String += codeUnit.description
    for word in codeUnit.words {
        units.append(UTF8.CodeUnit(y))
        }
    }

print("utf8String: \(utf8String)")

let newString = String(decoding: units, as: UTF8.self)
print("From utf8String: \(newString)")

Выход:

Initial string: This is a String ç´´Ω
utf8String: 16920618019418019416719532103110105114116833297321151053211510510484
From utf8String: This is a String ç´´Ω