[解決済み】なぜ、Swiftの文字列では、?のような絵文字が奇妙に扱われるのですか?
質問
字?????????????????????????????????????????????! (女2人、女1人、男1人の家族)はこのようにエンコードされます。
U+1F469
WOMAN
,
U+200D
ZWJ
,
U+1F469
WOMAN
,
U+200D
ZWJ
,
U+1F467
GIRL
,
U+200D
ZWJ
,
U+1F466
BOY
このように、非常に面白くエンコードされており、ユニットテストのターゲットとして最適です。しかし、Swiftはそれをどのように扱うかわからないようです。以下は、私が言いたいことです。
"????????????????".contains("????????????????") // true
"????????????????".contains("????") // false
"????????????????".contains("\u{200D}") // false
"????????????????".contains("????") // false
"????????????????".contains("????") // true
だから、Swiftはそれ自身(良い)と男の子(良い!)を含むと言います。しかし、それは、女性、女の子、またはゼロ幅ジョイナーを含んでいないと言います。 ここで何が起こっているのでしょうか?なぜSwiftは男の子が含まれていることを知っているのに、女性や女の子を含んでいないのでしょうか? 1つの文字として扱い、それ自体を含むと認識するだけなら理解できますが、1つのサブコンポーネントを得て、他は得られなかったという事実は私を困惑させます。
のようなものを使っても変わりません。
"????".characters.first!
.
さらに困惑するのは、このことです。
let manual = "\u{1F469}\u{200D}\u{1F469}\u{200D}\u{1F467}\u{200D}\u{1F466}"
Array(manual.characters) // ["????", "????", "????", "????"]
ZWJを配置しても、文字配列に反映されない。この後が少し気になりました。
manual.contains("????") // false
manual.contains("????") // false
manual.contains("????") // true
というわけで、文字配列でも同じ動作になります。配列がどのようなものか知っているので、この上なく腹立たしいことです。
のようなものを使っても、これは変わりません。
"????".characters.first!
.
解決方法は?
これは
String
型が Swift でどのように動作するか、そして
contains(_:)
メソッドが動作します。
この『? ' はいわゆる絵文字列で、文字列の中で目に見える1文字としてレンダリングされます。シーケンスの構成は
Character
オブジェクトで構成され、同時に
UnicodeScalar
オブジェクトを作成します。
文字列の文字数を調べると4文字で構成されていることがわかりますが、ユニコードのスカラー数を調べると、違う結果が表示されます。
print("????????????????".characters.count) // 4
print("????????????????".unicodeScalars.count) // 7
さて、文字を解析して印刷すると、一見普通の文字に見えますが、実は最初の3文字には絵文字とゼロ幅ジョイナーが含まれています。
UnicodeScalarView
:
for char in "????????????????".characters {
print(char)
let scalars = String(char).unicodeScalars.map({ String($0.value, radix: 16) })
print(scalars)
}
// ????
// ["1f469", "200d"]
// ????
// ["1f469", "200d"]
// ????
// ["1f467", "200d"]
// ????
// ["1f466"]
見ての通り、最後の文字だけがゼロ幅ジョイナーを含まないので、このような場合
contains(_:)
メソッドを使用すると、期待通りに動作します。ゼロ幅ジョイナーを含む絵文字と比較しているわけではないので、このメソッドは最後の文字以外では一致を見つけることができません。
これを発展させるために、もし
String
で構成された絵文字をゼロ幅ジョイナーで終了させ、それを
contains(_:)
メソッドを呼び出すと、その評価も
false
. これは
contains(_:)
と全く同じである。
range(of:) != nil
は、与えられた引数と完全に一致するものを探そうとする。ゼロ幅ジョイナーで終わる文字は不完全なシーケンスを形成するので、このメソッドはゼロ幅ジョイナーで終わる文字を完全なシーケンスに組み合わせながら引数に一致するものを探そうとします。これは、以下の場合、このメソッドは決してマッチを見つけられないことを意味します。
- 引数の末尾がゼロ幅ジョイナーで、かつ
- 解析する文字列が不完全なシーケンス(つまり、ゼロ幅ジョイナーで終わり、互換性のある文字が続いていない)を含んでいないことです。
デモをするために
let s = "\u{1f469}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}" // ????????????????
s.range(of: "\u{1f469}\u{200d}") != nil // false
s.range(of: "\u{1f469}\u{200d}\u{1f469}") != nil // false
しかし、比較は前方しか見ないので、後方から作業すれば、文字列内の他のいくつかの完全なシーケンスを見つけることができます。
s.range(of: "\u{1f466}") != nil // true
s.range(of: "\u{1f467}\u{200d}\u{1f466}") != nil // true
s.range(of: "\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}") != nil // true
// Same as the above:
s.contains("\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}") // true
最も簡単な解決策は、特定の比較オプションを
range(of:options:range:locale:)
メソッドを使用します。オプションの
String.CompareOptions.literal
は比較を
一文字ずつの正確な等価性
. 余談ですが、ここでいう「文字」というのは
ではなく
スウィフト号
Character
しかし、インスタンスと比較文字列の両方の UTF-16 表現です。
String
は不正な UTF-16 を許さないので、これは本質的に Unicode のスカラー表現を比較することと同じです。
ここでは
Foundation
メソッドがあるので、オリジナルのものが必要な場合は、このメソッドをリネームするなどしてください。
extension String {
func contains(_ string: String) -> Bool {
return self.range(of: string, options: String.CompareOptions.literal) != nil
}
}
これで、不完全な配列であっても、各文字に対してメソッドが "should"動作するようになりました。
s.contains("????") // true
s.contains("????\u{200d}") // true
s.contains("\u{200d}") // true
関連
-
[解決済み] Swiftでコアデータを日付(時間だけでなく)でソートする
-
[解決済み] ランダムな文字列を使用するこのコードは、なぜ "hello world" と表示されるのですか?
-
[解決済み] と'is'のどちらかを使って文字列を比較すると、異なる結果になることがあるのはなぜですか?
-
[解決済み】.NETで文字列が不変なら、なぜSubstringはO(n)時間かかるの?
-
[解決済み】文字列の最初のn文字を切り捨て、任意の文字が削除された場合は3つのドットを追加する
-
[解決済み】文字列の中に文字があるかどうかのテスト
-
[解決済み】Swift 4でStringのsubstringを使うには?'substring(to:)'は非推奨です。部分的な範囲から'演算子を持つ文字列スライス添字を使用してください。
-
[解決済み】SwiftでFloatをIntに変換する。
-
[解決済み】Swiftで文字列を連結する方法は?
-
[解決済み】swiftでDoubleを最も近いIntに丸めるには?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] 型のインスタンスでは静的メンバを使用できません。
-
[解決済み] このコンテキストで型検索を行う場合、「メソッド」は曖昧である、Alamofireのエラー
-
スレッド1:シグナルSIGABRTの問題解決
-
[解決済み] Swift - 呼出しの余分な引数
-
[解決済み] Swiftで#pragmaマーク?
-
[解決済み] 純粋な」Swift で弱いプロトコル参照を作るには (@objc なしで) どうしたらいいですか?
-
[解決済み] Swiftで乱数を生成する方法とは?
-
[解決済み】weak referenceとunowned referenceの違いは何ですか?
-
[解決済み】Swiftはドキュメント生成に対応しているのか?
-
[解決済み】ReactiveCocoaとRxSwiftの比較 - 長所と短所?