Есть ли способ создать строку из массива utf16 в Swift?
Мы знаем, что String.utf16 предоставляет codeunits или String.unicodeScalars предоставляет скаляры.
Если мы манипулируем codeunits и unicodeScales, удаляя некоторые элементы и т. Д., Есть ли способ вернуть полученную строку обратно?
4 ответа
Обновление для Swift 2.1:
Вы можете создать String
из массива символов UTF-16 с
public init(utf16CodeUnits: UnsafePointer<unichar>, count: Int)
инициализатор. Пример:
let str = "H€llo "
// String to UTF16 array:
let utf16array = Array(str.utf16)
print(utf16array)
// Output: [72, 8364, 108, 108, 111, 32, 55357, 56836]
// UTF16 array to string:
let str2 = String(utf16CodeUnits: utf16array, count: utf16array.count)
print(str2)
// H€llo
Предыдущий ответ:
Там нет ничего "встроенного" (насколько я знаю), но вы можете использовать UTF16
структура, которая обеспечивает decode()
метод:
extension String {
init?(utf16chars:[UInt16]) {
var str = ""
var generator = utf16chars.generate()
var utf16 : UTF16 = UTF16()
var done = false
while !done {
let r = utf16.decode(&generator)
switch (r) {
case .EmptyInput:
done = true
case let .Result(val):
str.append(Character(val))
case .Error:
return nil
}
}
self = str
}
}
Пример:
let str = "H€llo "
// String to UTF16 array:
let utf16array = Array(str.utf16)
print(utf16array)
// Output: [72, 8364, 108, 108, 111, 32, 55357, 56836]
// UTF16 array to string:
if let str2 = String(utf16chars: utf16array) {
print(str2)
// Output: H€llo
}
Чуть более универсально, вы можете определить метод, который создает строку из массива (или любой последовательности) кодовых точек, используя данный кодек:
extension String {
init?<S : SequenceType, C : UnicodeCodecType where S.Generator.Element == C.CodeUnit>
(codeUnits : S, var codec : C) {
var str = ""
var generator = codeUnits.generate()
var done = false
while !done {
let r = codec.decode(&generator)
switch (r) {
case .EmptyInput:
done = true
case let .Result(val):
str.append(Character(val))
case .Error:
return nil
}
}
self = str
}
}
Тогда преобразование из UTF16 выполняется как
if let str2a = String(codeUnits: utf16array, codec: UTF16()) {
print(str2a)
}
Вот еще одно возможное решение. В то время как предыдущие методы были "чистыми Swift", в этом используется платформа Foundation и автоматическое соединение между NSString
и Свифт String
:
extension String {
init?(utf16chars:[UInt16]) {
let data = NSData(bytes: utf16chars, length: utf16chars.count * sizeof(UInt16))
if let ns = NSString(data: data, encoding: NSUTF16LittleEndianStringEncoding) {
self = ns as String
} else {
return nil
}
}
}
Ответ так же прост, как:
/// An array of the UTF-16 for "Hello, world!".
let a: [UTF16.CodeUnit] = Array("Hello, world!".utf16)
/// A string representation of a, interpreted as UTF-16
let s = String(decoding: a, as: UTF16.self) // <=== The API you want
print(s)
Вот ответ @MartinR, обновленный до Swift 4
Первое решение должно импортировать фундамент:
import Foundation
extension String {
init(utf16chars: [UInt16]) {
self.init(utf16CodeUnits: utf16chars, count: utf16chars.count)
}
}
Другое решение, использующее только Swift (используя помеченный разрыв, как предложено в комментариях @Airspeed Velocity):
extension String {
init?(utf16chars : [UInt16]) {
var unicodeScalars: [Unicode.Scalar] = []
var iterator = utf16chars.makeIterator()
var utf16 = UTF16()
end: while true {
switch utf16.decode(&iterator) {
case .emptyInput:
break end
case .scalarValue(let unicodeScalar):
unicodeScalars.append(unicodeScalar)
case .error:
return nil
}
}
self.init(UnicodeScalarView(unicodeScalars))
}
}
Вот.
extension String {
static func fromUTF16Chars(utf16s:UInt16[]) -> String {
var str = ""
for var i = 0; i < utf16s.count; i++ {
let hi = Int(utf16s[i])
switch hi {
case 0xD800...0xDBFF:
let lo = Int(utf16s[++i])
let us = 0x10000
+ (hi - 0xD800)*0x400 + (lo - 0xDC00)
str += Character(UnicodeScalar(us))
default:
str += Character(UnicodeScalar(hi))
}
}
return str
}
}
let str = "aαあaαあ"
var utf16cs = UInt16[]()
for utf16c in str.utf16 {
utf16cs += utf16c
}
let str2 = String.fromUTF16Chars(utf16cs)
assert(str2 == str)
println(str2)