Bu, String
tü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 Character
nesnelerden oluşur ve aynı zamanda UnicodeScalar
nesnelerden 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, String
sı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.literal
karşılaştırmayı tam karakter karakter denkliği üzerinde gerçekleştirir . Yan not olarak, burada karakter ile kastedilen Swift değil , Character
hem örneğin hem de karşılaştırma dizesinin UTF-16 temsili - ancak, String
hatalı biçimlendirilmiş UTF-16'ya izin vermediğinden, bu aslında Unicode skalerlerin karşılaştırılmasına eşdeğerdir. temsilidir.
Burada Foundation
yö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