web-dev-qa-db-ja.com

SwiftでUTF8から文字列を作成するにはどうすればよいですか?

各文字をUTF8コード単位で印刷できることを知っていますか?次に、これらの文字のコード単位がある場合、それらを使用して文字列を作成するにはどうすればよいですか?

18
jxwho

UTF8コードポイントをUTF8 Swiftクラスを使用して慣用的にSwift Stringに変換できます。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を使用すると、複雑な書記素クラスターを無視して、UnicodeScalarを直接文字に変換するため、エラーが発生しやすくなります。そして

  • String.fromCStringはポインターを使用するため、潜在的に安全ではありません。

14
Tim WB

Swift 5では、UTF-8コード単位のコレクションを文字列に変換するために、次のいずれかの方法を選択できます。


#1。 Stringの使用 init(_:) 初期化子

_String.UTF8View_インスタンス(つまり、UTF-8コード単位のコレクション)があり、それを文字列に変換する場合は、init(_:)イニシャライザを使用できます。 init(_:)には次の宣言があります。

_init(_ utf8: String.UTF8View)
_

指定されたUTF-8コード単位のシーケンスに対応する文字列を作成します。

以下のPlaygroundサンプルコードは、init(_:)の使用方法を示しています。

_let string = "Café ????????"
let utf8View: String.UTF8View = string.utf8

let newString = String(utf8View)
print(newString) // prints: Café ????????
_

#2。 Swiftの使用 init(decoding:as:) 初期化子

init(decoding:as:)は、指定されたエンコードで、指定されたUnicodeコード単位コレクションから文字列を作成します。

_let string = "Café ????????"
let codeUnits: [Unicode.UTF8.CodeUnit] = Array(string.utf8)

let newString = String(decoding: codeUnits, as: UTF8.self)
print(newString) // prints: Café ????????
_

init(decoding:as:)は_String.UTF8View_パラメータでも機能することに注意してください。

_let string = "Café ????????"
let utf8View: String.UTF8View = string.utf8

let newString = String(decoding: utf8View, as: UTF8.self)
print(newString) // prints: Café ????????
_

#3。 transcode(_:from:to:stoppingOnError:into:) 関数を使用する

次の例では、初期文字列のUTF-8表現を、新しい文字列の作成に使用できるUnicodeスカラー値(UTF-32コード単位)にトランスコードします。

_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é ????????
_

#4。 Arrayの-​​ withUnsafeBufferPointer(_:) メソッドとStringの-​​ init(cString:) 初期化子を使用する

init(cString:)には次の宣言があります。

_init(cString: UnsafePointer<CChar>)
_

指定されたポインタによって参照されるnullで終了するUTF-8データをコピーして、新しい文字列を作成します。

次の例は、init(cString:)を使用して、CChar配列のコンテンツ(つまり、整形式のUTF-8コードユニットシーケンス)へのポインターを指定し、そこから文字列を作成する方法を示しています。 :

_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é ????????
_

#5。 _Unicode.UTF8_の decode(_:) メソッドの使用

コードユニットシーケンスをデコードするには、_UnicodeDecodingResult.emptyInput_が返されるまでdecode(_:)を繰り返し呼び出します。

_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é ????????
_

#6。 Stringの使用 init(bytes:encoding:) 初期化子

FoundationはStringinit(bytes:encoding:)イニシャライザを提供します。これは、以下の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é ????????")
_
6
Imanou Petit

マーティン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)を使用して文字列を作成できます。

5
Bryan Chen

Swift

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

4
Alex Shubin

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"
2
dbart
// Swift4
var units = [UTF8.CodeUnit]()
//
// update units
//
let str = String(decoding: units, as: UTF8.self)
2
Qinghua

これは可能な解決策です(現在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") 
}

クロージャ内で、$0UnsafeBufferPointer<CChar>配列の連続したストレージを指します。それからa Swift Stringを作成できます。

または、入力をunsignedバイトにしたい場合:

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")
}
1
Martin R

ファイルハンドルから返された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)
1
johnkzin

私はこのようなことをするでしょう、それは「ポインタ」を扱うよりもそれほどエレガントではないかもしれませんが、それはうまく機能します、それらは新しい+=演算子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にユニコード(UTF-8、UTF-16またはUTF-32)文字を追加することができます。例:

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

Swift 3.0バージョンの Martin R 回答があります

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
  }
}

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
  }
}
0