Bu, Stringtürün Swift'te nasıl çalıştığı ve contains(_:)yöntemin nasıl çalıştığı ile ilgilidir.
'👩👩👧👦', bir dizede görünür bir karakter olarak gösterilen emoji dizisi olarak bilinen şeydir. Sekans Characternesnelerden oluşur ve aynı zamanda UnicodeScalarnesnelerden oluşur.
Dizenin karakter sayısını kontrol ederseniz, dört karakterden oluştuğunu görürsünüz, unicode skaler sayısını kontrol ederseniz, size farklı bir sonuç gösterir:
print("👩👩👧👦".characters.count) // 4
print("👩👩👧👦".unicodeScalars.count) // 7
Şimdi, karakterleri ayrıştırır ve yazdırırsanız, normal karakterlere benzeyen şeyleri görürsünüz, ancak aslında ilk üç karakter hem emoji hem de sıfır genişlikli bir birleştirici içerir 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"]
Gördüğünüz gibi, yalnızca son karakter sıfır genişlikli bir birleştirici içermez, bu nedenle contains(_:)yöntemi kullanırken beklediğiniz gibi çalışır. Sıfır genişlikli birleştiriciler içeren emoji ile karşılaştırma yapmadığınız için, yöntem son karakterden başka bir eşleşme bulamaz.
Bunu genişletmek için, Stringsıfır genişlikli bir birleştirici ile biten bir emoji karakterinden oluşan bir contains(_:)yöntem oluşturursanız ve yönteme iletirseniz, bunu da değerlendirir false. Bu, verilen argümanla tam olarak eşleşmeye çalışan ile contains(_:)tam olarak aynı olmakla ilgilidir range(of:) != nil. Sıfır genişlikli bir birleştirici ile biten karakterler eksik bir dizi oluşturduğundan, yöntem, sıfır genişlikli birleştiricilerle biten karakterleri tam bir dizide birleştirirken argüman için bir eşleşme bulmaya çalışır. Bu, yöntemin aşağıdaki durumlarda hiç bir eşleşme bulamayacağı anlamına gelir:
- argüman sıfır genişlikli bir birleştirici ile sona erer ve
- ayrıştırılacak dize eksik bir dizi içermiyor (yani sıfır genişlikli bir birleştirici ile biten ve ardından uyumlu bir karakter içermeyen).
Göstermek:
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
Ancak, karşılaştırma yalnızca ileriye baktığından, dize içinde geriye doğru çalışarak birkaç tam diziyi daha bulabilirsiniz:
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
En kolay çözüm, range(of:options:range:locale:)yönteme özel bir karşılaştırma seçeneği sunmak olacaktır . Seçenek String.CompareOptions.literalkarşılaştırmayı tam karakter karakter denkliği üzerinde gerçekleştirir . Yan not olarak, burada karakter ile kastedilen Swift değil , Characterhem örneğin hem de karşılaştırma dizesinin UTF-16 temsili - ancak, Stringhatalı biçimlendirilmiş UTF-16'ya izin vermediğinden, bu aslında Unicode skalerlerin karşılaştırılmasına eşdeğerdir. temsilidir.
Burada Foundationyöntemi aşırı yükledim, bu yüzden orijinaline ihtiyacınız varsa, bunu veya bir şeyi yeniden adlandırın:
extension String {
func contains(_ string: String) -> Bool {
return self.range(of: string, options: String.CompareOptions.literal) != nil
}
}
Şimdi yöntem, tamamlanmamış dizilerle bile her karakterle "olması gerektiği gibi" çalışır:
s.contains("👩") // true
s.contains("👩\u{200d}") // true
s.contains("\u{200d}") // true