Nous savons que nous pouvons imprimer chaque caractère en unités de code UTF8? Ensuite, si nous avons des unités de code de ces caractères, comment créer une chaîne avec eux?
Il est possible de convertir les points de code UTF8 en chaîne Swift de manière idiomatique à l'aide de la classe UTF8
Swift. Bien qu'il soit beaucoup plus facile de convertir de String en 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)")
}
Parmi les autres alternatives proposées:
Utiliser NSString
appelle le pont Objective-C;
Utiliser UnicodeScalar
est sujet aux erreurs car il convertit directement UnicodeScalars en caractères, en ignorant les grappes de graphèmes complexes; et
Utiliser String.fromCString
est potentiellement dangereux car il utilise des pointeurs.
améliorer la réponse de Martin 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: €
Ce qui s’est passé est que Array
peut être converti automatiquement en CConstVoidPointer
qui peut être utilisé pour créer une chaîne avec NSSString(bytes: CConstVoidPointer, length len: Int, encoding: Uint)
Je cherchais moi-même une réponse complète concernant la manipulation de cordes à Swift. S'appuyer sur le casting de et vers NSString
et d'autres magies de pointeur non sécurisées ne le faisaient tout simplement pas pour moi. Voici une alternative sûre:
D'abord, nous voudrons étendre UInt8
. C'est le type primitif derrière CodeUnit
.
extension UInt8 {
var character: Character {
return Character(UnicodeScalar(self))
}
}
Cela nous permettra de faire quelque chose comme ceci:
let codeUnits: [UInt8] = [
72, 69, 76, 76, 79
]
let characters = codeUnits.map { $0.character }
let string = String(characters)
// string prints "HELLO"
Equipé de cette extension, nous pouvons maintenant modifier des chaînes.
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"
Voici une solution possible (mise à jour pour 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")
}
Dans la fermeture, $0
est un UnsafeBufferPointer<CChar>
pointant vers le stockage contigu du tableau. À partir de cela, une String
rapide peut être créée.
Sinon, si vous préférez l’entrée sous forme de unsigned octets:
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")
}
// Swift4
var units = [UTF8.CodeUnit]()
//
// update units
//
let str = String(decoding: units, as: UTF8.self)
Si vous commencez avec un tampon brut, tel que celui provenant de l'objet Data renvoyé par un descripteur de fichier (dans ce cas, extrait d'un objet 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)
Je ferais quelque chose comme ça, ce n'est peut-être pas si élégant que de travailler avec des "pointeurs", mais ça fait bien le travail, il s'agit plutôt d'un groupe de nouveaux opérateurs +=
pour String
comme:
@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)))
}
REMARQUE: vous pouvez également étendre la liste des opérateurs pris en charge avec l'opérateur de substitution +
, en définissant la liste des opérateurs entièrement commutatifs pour String
.
et maintenant vous pouvez ajouter une String
avec un caractère unicode (UTF-8, UTF-16 ou UTF-32) comme par exemple:
var string: String = "signs of the Zodiac: "
string += (0x0, 0x0, 0x26, 0x4b)
string += (38)
string += (0x26, 76)
Avec Swift 5, vous pouvez choisir l’une des méthodes suivantes pour convertir une collection d’unités de code UTF-8 en chaîne.
String
's init(_:)
initializerSi vous avez une instance String.UTF8View
(c'est-à-dire une collection d'unités de code UTF-8) et souhaitez la convertir en chaîne, vous pouvez utiliser l'initialiseur init(_:)
. init(_:)
a la déclaration suivante:
init(_ utf8: String.UTF8View)
Crée une chaîne correspondant à la séquence donnée des unités de code UTF-8.
L'exemple de code Playground ci-dessous montre comment utiliser init(_:)
:
let string = "Café ????????"
let utf8View: String.UTF8View = string.utf8
let newString = String(utf8View)
print(newString) // prints: Café ????????
Swift
's init(decoding:as:)
initializerinit(decoding:as:)
crée une chaîne à partir de la collection d'unités de code Unicode donnée dans le codage spécifié:
let string = "Café ????????"
let codeUnits: [Unicode.UTF8.CodeUnit] = Array(string.utf8)
let newString = String(decoding: codeUnits, as: UTF8.self)
print(newString) // prints: Café ????????
Notez que init(decoding:as:)
fonctionne également avec le paramètre String.UTF8View
:
let string = "Café ????????"
let utf8View: String.UTF8View = string.utf8
let newString = String(decoding: utf8View, as: UTF8.self)
print(newString) // prints: Café ????????
transcode(_:from:to:stoppingOnError:into:)
functionL'exemple suivant convertit la représentation UTF-8 d'une chaîne initiale en valeurs scalaires Unicode (unités de code UTF-32) pouvant être utilisées pour créer une nouvelle chaîne:
let string = "Café ????????"
let bytes = Array(string.utf8)
var newString = ""
_ = transcode(bytes.makeIterator(), from: UTF8.self, to: UTF32.self, stoppingOnError: true, into: {
newString.append(String(Unicode.Scalar($0)!))
})
print(newString) // prints: Café ????????
Array
's withUnsafeBufferPointer(_:)
method et de String
' s init(cString:)
initializerinit(cString:)
a la déclaration suivante:
init(cString: UnsafePointer<CChar>)
Crée une nouvelle chaîne en copiant les données UTF-8 à zéro terminal référencées par le pointeur donné.
L'exemple suivant montre comment utiliser init(cString:)
avec un pointeur sur le contenu d'un tableau CChar
(c'est-à-dire une séquence d'unités de code UTF-8 bien formée) afin de créer une chaîne à partir de celui-ci:
let bytes: [CChar] = [67, 97, 102, -61, -87, 32, -16, -97, -121, -85, -16, -97, -121, -73, 0]
let newString = bytes.withUnsafeBufferPointer({ (bufferPointer: UnsafeBufferPointer<CChar>)in
return String(cString: bufferPointer.baseAddress!)
})
print(newString) // prints: Café ????????
Unicode.UTF8
's decode(_:)
methodPour décoder une séquence d'unités de code, appelez decode(_:)
à plusieurs reprises jusqu'à ce qu'il renvoie UnicodeDecodingResult.emptyInput
:
let string = "Café ????????"
let codeUnits = Array(string.utf8)
var codeUnitIterator = codeUnits.makeIterator()
var utf8Decoder = Unicode.UTF8()
var newString = ""
Decode: while true {
switch utf8Decoder.decode(&codeUnitIterator) {
case .scalarValue(let value):
newString.append(Character(Unicode.Scalar(value)))
case .emptyInput:
break Decode
case .error:
print("Decoding error")
break Decode
}
}
print(newString) // prints: Café ????????
String
's init(bytes:encoding:)
initializerFoundation fournit à String
un initialiseur init(bytes:encoding:)
que vous pouvez utiliser comme indiqué dans l'exemple de code ci-dessous de Playground:
import Foundation
let string = "Café ????????"
let bytes: [Unicode.UTF8.CodeUnit] = Array(string.utf8)
let newString = String(bytes: bytes, encoding: String.Encoding.utf8)
print(String(describing: newString)) // prints: Optional("Café ????????")
Il existe une version Swift 3.0 de 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
}
}
Si vous souhaitez afficher emoji à partir de la chaîne UTF-8, utilisez uniquement la méthode utilisateur convertEmojiCodesToString ci-dessous. Il fonctionne correctement pour des chaînes telles que "U + 1F52B" (emoji) ou "U + 1F1E6 U + 1F1F1" (pays emoji).
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
}
}