モンゴル語の詳細に興味はないが、SwiftでUnicode値を使用および変換することについての簡単な回答が必要な場合は、 承認された回答 の最初の部分までスキップしてください。
iOSアプリで使用される伝統的なモンゴル語 のUnicodeテキストをレンダリングしたい。より良い長期的な解決策は、この複雑なスクリプトをレンダリングする AATスマートフォント を使用することです。 ( そのようなフォントは存在します ですが、そのライセンスでは変更や非個人的な使用は許可されていません。)ただし、AATフォントのすべてのレンダリングロジックは言うまでもなく、フォントを作成したことがないため、今のところSwiftで自分でレンダリングする予定です。後日、スマートフォントの作成方法を学習できるかもしれません。
外部ではUnicodeテキストを使用しますが、内部では(UITextView
で表示するために)Unicodeをダムフォント(Unicode [〜#〜] puaでコード化)に格納されている個々のグリフに変換します[〜#〜] 値)。したがって、レンダリングエンジンは、モンゴルのUnicode値(範囲:U + 1820からU + 1842)をPUAに格納されているグリフ値(範囲:U + E360からU + E5CF)に変換する必要があります。とにかく、これは 過去にJavaでやったこと なので、これが私の計画です。しかし、多分私は全体の考え方を変える必要があります。
次の画像は、文字u(赤)に2つの異なる形式を使用してモンゴル語で2回書かれたsuを示しています。 (モンゴル語は縦書きで、英語の筆記体のように文字がつながっています。)
Unicodeでは、これらの2つの文字列は次のように表現されます。
var suForm1: String = "\u{1830}\u{1826}"
var suForm2: String = "\u{1830}\u{1826}\u{180B}"
suForm2
のFree Variation Selector(U + 180B)は、Swift String
によってu(U + 1826)その前。 Swiftは単一の文字、拡張書記素クラスターと見なします。ただし、自分でレンダリングを行うために、u(U + 1826)とFVS1(U + 180B)を2つの異なるUTF-16コードポイントとして区別する必要があります。
内部表示のために、上記のUnicode文字列を次のレンダリングされたグリフ文字列に変換します。
suForm1 = "\u{E46F}\u{E3BA}"
suForm2 = "\u{E46F}\u{E3BB}"
私はSwift String
とCharacter
で遊んでいます。それらには多くの便利なものがありますが、私の特定のケースではUTF-16コードユニットのみを扱っているため、SwiftのNSString
ではなく古いString
を使用する必要があるのでしょうか。 String.utf16
を使用してUTF-16コードポイントを取得できることを理解していますが、 String
への変換はあまり良くありません 。
String
と Character
に固執する方が良いでしょうか、または NSString
を使用する必要がありますか=および unichar
?
ページをクリーンアップするために、この質問の更新は非表示になっています。編集履歴を参照してください。
Swift 3に更新
将来この質問にアクセスするほとんどすべての人にとって、 String
とCharacter
が答えになります。
コードにUnicode値を直接設定します:
var str: String = "I want to visit 北京, Москва, मुंबई, القاهرة, and 서울시. ????"
var character: Character = "????"
16進数を使用して値を設定します
var str: String = "\u{61}\u{5927}\u{1F34E}\u{3C0}" // a大????π
var character: Character = "\u{65}\u{301}" // é = "e" + accent mark
Swift文字は複数のUnicodeコードポイントで構成できますが、単一の文字のように見えます。これは拡張書記クラスタと呼ばれます。
この質問 も参照してください。
Unicode値に変換:
str.utf8
str.utf16
str.unicodeScalars // UTF-32
String(character).utf8
String(character).utf16
String(character).unicodeScalars
Unicode 16進数値からの変換:
let hexValue: UInt32 = 0x1F34E
// convert hex value to UnicodeScalar
guard let scalarValue = UnicodeScalar(hexValue) else {
// early exit if hex does not form a valid unicode value
return
}
// convert UnicodeScalar to String
let myString = String(scalarValue) // ????
または、代わりに:
let hexValue: UInt32 = 0x1F34E
if let scalarValue = UnicodeScalar(hexValue) {
let myString = String(scalarValue)
}
さらにいくつかの例
let value0: UInt8 = 0x61
let value1: UInt16 = 0x5927
let value2: UInt32 = 0x1F34E
let string0 = String(UnicodeScalar(value0)) // a
let string1 = String(UnicodeScalar(value1)) // 大
let string2 = String(UnicodeScalar(value2)) // ????
// convert hex array to String
let myHexArray = [0x43, 0x61, 0x74, 0x203C, 0x1F431] // an Int array
var myString = ""
for hexValue in myHexArray {
myString.append(UnicodeScalar(hexValue))
}
print(myString) // Cat‼????
UTF-8およびUTF-16の場合、変換は必ずしも簡単ではないことに注意してください。 ( TF-8 、 TF-16 、および TF-32 の質問を参照してください。)
SwiftでNSString
とunichar
を使用することもできますが、Objective Cに精通していて、構文をSwiftに変換するのが得意でない限り、見つけるのは難しいでしょう。優れたドキュメント。
また、unichar
はUInt16
配列であり、上記のようにUInt16
からUnicodeスカラー値への変換は必ずしも容易ではありません(つまり、絵文字やその他の文字の代用ペアの変換上のコードプレーン)。
質問で言及された理由により、私は上記の方法のいずれも使用しなくなりました。代わりに、Unicodeスカラー値を保持するUInt32
の配列である独自の文字列構造を作成しました。
繰り返しますが、これはほとんどの人にとっての解決策ではありません。 String
またはCharacter
の機能を少しだけ拡張する必要がある場合は、まず extensions の使用を検討してください。
ただし、Unicodeスカラー値を排他的に使用する必要がある場合は、カスタム構造体を作成できます。
利点は次のとおりです。
String
、Character
、UnicodeScalar
、UInt32
など)を絶えず切り替える必要はありません。String
への最終的な変換は簡単です。欠点は次のとおりです。
独自に作成することもできますが、参照用にここにあります。最も難しい部分は Hashableにする でした。
// This struct is an array of UInt32 to hold Unicode scalar values
// Version 3.4.0 (Swift 3 update)
struct ScalarString: Sequence, Hashable, CustomStringConvertible {
fileprivate var scalarArray: [UInt32] = []
init() {
// does anything need to go here?
}
init(_ character: UInt32) {
self.scalarArray.append(character)
}
init(_ charArray: [UInt32]) {
for c in charArray {
self.scalarArray.append(c)
}
}
init(_ string: String) {
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// Generator in order to conform to SequenceType protocol
// (to allow users to iterate as in `for myScalarValue in myScalarString` { ... })
func makeIterator() -> AnyIterator<UInt32> {
return AnyIterator(scalarArray.makeIterator())
}
// append
mutating func append(_ scalar: UInt32) {
self.scalarArray.append(scalar)
}
mutating func append(_ scalarString: ScalarString) {
for scalar in scalarString {
self.scalarArray.append(scalar)
}
}
mutating func append(_ string: String) {
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// charAt
func charAt(_ index: Int) -> UInt32 {
return self.scalarArray[index]
}
// clear
mutating func clear() {
self.scalarArray.removeAll(keepingCapacity: true)
}
// contains
func contains(_ character: UInt32) -> Bool {
for scalar in self.scalarArray {
if scalar == character {
return true
}
}
return false
}
// description (to implement Printable protocol)
var description: String {
return self.toString()
}
// endsWith
func endsWith() -> UInt32? {
return self.scalarArray.last
}
// indexOf
// returns first index of scalar string match
func indexOf(_ string: ScalarString) -> Int? {
if scalarArray.count < string.length {
return nil
}
for i in 0...(scalarArray.count - string.length) {
for j in 0..<string.length {
if string.charAt(j) != scalarArray[i + j] {
break // substring mismatch
}
if j == string.length - 1 {
return i
}
}
}
return nil
}
// insert
mutating func insert(_ scalar: UInt32, atIndex index: Int) {
self.scalarArray.insert(scalar, at: index)
}
mutating func insert(_ string: ScalarString, atIndex index: Int) {
var newIndex = index
for scalar in string {
self.scalarArray.insert(scalar, at: newIndex)
newIndex += 1
}
}
mutating func insert(_ string: String, atIndex index: Int) {
var newIndex = index
for scalar in string.unicodeScalars {
self.scalarArray.insert(scalar.value, at: newIndex)
newIndex += 1
}
}
// isEmpty
var isEmpty: Bool {
return self.scalarArray.count == 0
}
// hashValue (to implement Hashable protocol)
var hashValue: Int {
// DJB Hash Function
return self.scalarArray.reduce(5381) {
($0 << 5) &+ $0 &+ Int($1)
}
}
// length
var length: Int {
return self.scalarArray.count
}
// remove character
mutating func removeCharAt(_ index: Int) {
self.scalarArray.remove(at: index)
}
func removingAllInstancesOfChar(_ character: UInt32) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar != character {
returnString.append(scalar)
}
}
return returnString
}
func removeRange(_ range: CountableRange<Int>) -> ScalarString? {
if range.lowerBound < 0 || range.upperBound > scalarArray.count {
return nil
}
var returnString = ScalarString()
for i in 0..<scalarArray.count {
if i < range.lowerBound || i >= range.upperBound {
returnString.append(scalarArray[i])
}
}
return returnString
}
// replace
func replace(_ character: UInt32, withChar replacementChar: UInt32) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar == character {
returnString.append(replacementChar)
} else {
returnString.append(scalar)
}
}
return returnString
}
func replace(_ character: UInt32, withString replacementString: String) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar == character {
returnString.append(replacementString)
} else {
returnString.append(scalar)
}
}
return returnString
}
func replaceRange(_ range: CountableRange<Int>, withString replacementString: ScalarString) -> ScalarString {
var returnString = ScalarString()
for i in 0..<scalarArray.count {
if i < range.lowerBound || i >= range.upperBound {
returnString.append(scalarArray[i])
} else if i == range.lowerBound {
returnString.append(replacementString)
}
}
return returnString
}
// set (an alternative to myScalarString = "some string")
mutating func set(_ string: String) {
self.scalarArray.removeAll(keepingCapacity: false)
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// split
func split(atChar splitChar: UInt32) -> [ScalarString] {
var partsArray: [ScalarString] = []
if self.scalarArray.count == 0 {
return partsArray
}
var part: ScalarString = ScalarString()
for scalar in self.scalarArray {
if scalar == splitChar {
partsArray.append(part)
part = ScalarString()
} else {
part.append(scalar)
}
}
partsArray.append(part)
return partsArray
}
// startsWith
func startsWith() -> UInt32? {
return self.scalarArray.first
}
// substring
func substring(_ startIndex: Int) -> ScalarString {
// from startIndex to end of string
var subArray: ScalarString = ScalarString()
for i in startIndex..<self.length {
subArray.append(self.scalarArray[i])
}
return subArray
}
func substring(_ startIndex: Int, _ endIndex: Int) -> ScalarString {
// (startIndex is inclusive, endIndex is exclusive)
var subArray: ScalarString = ScalarString()
for i in startIndex..<endIndex {
subArray.append(self.scalarArray[i])
}
return subArray
}
// toString
func toString() -> String {
var string: String = ""
for scalar in self.scalarArray {
if let validScalor = UnicodeScalar(scalar) {
string.append(Character(validScalor))
}
}
return string
}
// trim
// removes leading and trailing whitespace (space, tab, newline)
func trim() -> ScalarString {
//var returnString = ScalarString()
let space: UInt32 = 0x00000020
let tab: UInt32 = 0x00000009
let newline: UInt32 = 0x0000000A
var startIndex = self.scalarArray.count
var endIndex = 0
// leading whitespace
for i in 0..<self.scalarArray.count {
if self.scalarArray[i] != space &&
self.scalarArray[i] != tab &&
self.scalarArray[i] != newline {
startIndex = i
break
}
}
// trailing whitespace
for i in stride(from: (self.scalarArray.count - 1), through: 0, by: -1) {
if self.scalarArray[i] != space &&
self.scalarArray[i] != tab &&
self.scalarArray[i] != newline {
endIndex = i + 1
break
}
}
if endIndex <= startIndex {
return ScalarString()
}
return self.substring(startIndex, endIndex)
}
// values
func values() -> [UInt32] {
return self.scalarArray
}
}
func ==(left: ScalarString, right: ScalarString) -> Bool {
return left.scalarArray == right.scalarArray
}
func +(left: ScalarString, right: ScalarString) -> ScalarString {
var returnString = ScalarString()
for scalar in left.values() {
returnString.append(scalar)
}
for scalar in right.values() {
returnString.append(scalar)
}
return returnString
}
//Swift 3.0
// This struct is an array of UInt32 to hold Unicode scalar values
struct ScalarString: Sequence, Hashable, CustomStringConvertible {
private var scalarArray: [UInt32] = []
init() {
// does anything need to go here?
}
init(_ character: UInt32) {
self.scalarArray.append(character)
}
init(_ charArray: [UInt32]) {
for c in charArray {
self.scalarArray.append(c)
}
}
init(_ string: String) {
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// Generator in order to conform to SequenceType protocol
// (to allow users to iterate as in `for myScalarValue in myScalarString` { ... })
//func generate() -> AnyIterator<UInt32> {
func makeIterator() -> AnyIterator<UInt32> {
let nextIndex = 0
return AnyIterator {
if (nextIndex > self.scalarArray.count-1) {
return nil
}
return self.scalarArray[nextIndex + 1]
}
}
// append
mutating func append(scalar: UInt32) {
self.scalarArray.append(scalar)
}
mutating func append(scalarString: ScalarString) {
for scalar in scalarString {
self.scalarArray.append(scalar)
}
}
mutating func append(string: String) {
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// charAt
func charAt(index: Int) -> UInt32 {
return self.scalarArray[index]
}
// clear
mutating func clear() {
self.scalarArray.removeAll(keepingCapacity: true)
}
// contains
func contains(character: UInt32) -> Bool {
for scalar in self.scalarArray {
if scalar == character {
return true
}
}
return false
}
// description (to implement Printable protocol)
var description: String {
var string: String = ""
for scalar in scalarArray {
string.append(String(describing: UnicodeScalar(scalar))) //.append(UnicodeScalar(scalar)!)
}
return string
}
// endsWith
func endsWith() -> UInt32? {
return self.scalarArray.last
}
// insert
mutating func insert(scalar: UInt32, atIndex index: Int) {
self.scalarArray.insert(scalar, at: index)
}
// isEmpty
var isEmpty: Bool {
get {
return self.scalarArray.count == 0
}
}
// hashValue (to implement Hashable protocol)
var hashValue: Int {
get {
// DJB Hash Function
var hash = 5381
for i in 0 ..< scalarArray.count {
hash = ((hash << 5) &+ hash) &+ Int(self.scalarArray[i])
}
/*
for i in 0..< self.scalarArray.count {
hash = ((hash << 5) &+ hash) &+ Int(self.scalarArray[i])
}
*/
return hash
}
}
// length
var length: Int {
get {
return self.scalarArray.count
}
}
// remove character
mutating func removeCharAt(index: Int) {
self.scalarArray.remove(at: index)
}
func removingAllInstancesOfChar(character: UInt32) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar != character {
returnString.append(scalar: scalar) //.append(scalar)
}
}
return returnString
}
// replace
func replace(character: UInt32, withChar replacementChar: UInt32) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar == character {
returnString.append(scalar: replacementChar) //.append(replacementChar)
} else {
returnString.append(scalar: scalar) //.append(scalar)
}
}
return returnString
}
// func replace(character: UInt32, withString replacementString: String) -> ScalarString {
func replace(character: UInt32, withString replacementString: ScalarString) -> ScalarString {
var returnString = ScalarString()
for scalar in self.scalarArray {
if scalar == character {
returnString.append(scalarString: replacementString) //.append(replacementString)
} else {
returnString.append(scalar: scalar) //.append(scalar)
}
}
return returnString
}
// set (an alternative to myScalarString = "some string")
mutating func set(string: String) {
self.scalarArray.removeAll(keepingCapacity: false)
for s in string.unicodeScalars {
self.scalarArray.append(s.value)
}
}
// split
func split(atChar splitChar: UInt32) -> [ScalarString] {
var partsArray: [ScalarString] = []
var part: ScalarString = ScalarString()
for scalar in self.scalarArray {
if scalar == splitChar {
partsArray.append(part)
part = ScalarString()
} else {
part.append(scalar: scalar) //.append(scalar)
}
}
partsArray.append(part)
return partsArray
}
// startsWith
func startsWith() -> UInt32? {
return self.scalarArray.first
}
// substring
func substring(startIndex: Int) -> ScalarString {
// from startIndex to end of string
var subArray: ScalarString = ScalarString()
for i in startIndex ..< self.length {
subArray.append(scalar: self.scalarArray[i]) //.append(self.scalarArray[i])
}
return subArray
}
func substring(startIndex: Int, _ endIndex: Int) -> ScalarString {
// (startIndex is inclusive, endIndex is exclusive)
var subArray: ScalarString = ScalarString()
for i in startIndex ..< endIndex {
subArray.append(scalar: self.scalarArray[i]) //.append(self.scalarArray[i])
}
return subArray
}
// toString
func toString() -> String {
let string: String = ""
for scalar in self.scalarArray {
string.appending(String(describing:UnicodeScalar(scalar))) //.append(UnicodeScalar(scalar)!)
}
return string
}
// values
func values() -> [UInt32] {
return self.scalarArray
}
}
func ==(left: ScalarString, right: ScalarString) -> Bool {
if left.length != right.length {
return false
}
for i in 0 ..< left.length {
if left.charAt(index: i) != right.charAt(index: i) {
return false
}
}
return true
}
func +(left: ScalarString, right: ScalarString) -> ScalarString {
var returnString = ScalarString()
for scalar in left.values() {
returnString.append(scalar: scalar) //.append(scalar)
}
for scalar in right.values() {
returnString.append(scalar: scalar) //.append(scalar)
}
return returnString
}