์บ๋ฆญํฐ ๐ฉโ๐ฉโ๐งโ๐ฆ (์ฌ์ฑ 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๋ ์์ฒด (์ข์)์ ์๋ (์ข์!)์ ํฌํจํ๊ณ ์๋ค๊ณ ๋งํฉ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ์ฌ์, ์ฌ์ ๋๋ ํญ์ด 0 ์ธ ๊ฒฐํฉ์๋ฅผ ํฌํจํ์ง ์๋๋ค๊ณ ๋งํฉ๋๋ค. ์ฌ๊ธฐ์ ๋ฌด์จ ์ผ์ด์ผ? ์ Swift๋ ๋จ์๋ ์์ง๋ง ์ฌ์ ๋ ์ฌ์๋ ํฌํจํ์ง ์๋๋ค๊ณ ์๊ณ ์์ต๋๊น? ๊ทธ๊ฒ์ด ํ๋์ ๋ฌธ์๋ก ์ทจ๊ธํ๊ณ ์์ ์ ํฌํจํ๊ณ ์๋ ๊ฒ์ ์ธ์ํ๋ค๋ฉด ์ดํดํ ์ ์์์ง๋ง ํ๋์ ํ์ ๊ตฌ์ฑ ์์๊ฐ ์๊ณ ๋ค๋ฅธ ๊ตฌ์ฑ ์์๊ฐ ์๋ค๋ ์ฌ์ค์ด ๋๋ฅผ ๋ฐฉํดํฉ๋๋ค.
๋ด๊ฐ ๊ฐ์ ๊ฒ์ ์ฌ์ฉํ๋ฉด ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค "๐ฉ".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(_:)
๋ฐฉ์ ๋ฐ ๋ฐฉ๋ฒ์ด ์๋ ํ๋ ๋ฐฉ์๊ณผ ๊ด๋ จ์ด ์์ต๋๋ค.
‘๐ฉโ๐ฉโ๐งโ๐ฆ’์ ์ด๋ชจ ์ง ์ํ์ค๋ก ์๋ ค์ง ๊ฒ์ผ๋ก ๋ฌธ์์ด์์ ํ๋์ ๋ณด์ด๋ ๋ฌธ์๋ก ๋ ๋๋ง๋ฉ๋๋ค. ์ํ์ค๋ Character
๊ฐ์ฒด ๋ก ๊ตฌ์ฑ ๋๋ฉฐ ๋์์ UnicodeScalar
๊ฐ์ฒด ๋ก ๊ตฌ์ฑ ๋ฉ๋๋ค.
๋ฌธ์์ด์ ๋ฌธ์ ์๋ฅผ ํ์ธํ๋ฉด 4 ๊ฐ์ ๋ฌธ์๋ก ๊ตฌ์ฑ๋๋ฉฐ ์ ๋ ์ฝ๋ ์ค์นผ๋ผ ์๋ฅผ ํ์ธํ๋ฉด ๋ค๋ฅธ ๊ฒฐ๊ณผ๊ฐ ํ์๋ฉ๋๋ค.
print("๐ฉโ๐ฉโ๐งโ๐ฆ".characters.count) // 4
print("๐ฉโ๐ฉโ๐งโ๐ฆ".unicodeScalars.count) // 7
์ด์ ๋ฌธ์๋ฅผ ํ์ฑํ๊ณ ์ธ์ํ๋ฉด ์ผ๋ฐ ๋ฌธ์์ฒ๋ผ ๋ณด์ด์ง๋ง ์ค์ ๋ก๋ ์ฒซ ๋ฒ์งธ ์ธ ๋ฌธ์์๋ ์ด๋ชจํฐ์ฝ๊ณผ ๋๋น๊ฐ 0 ์ธ ์กฐ์ด๋๊ฐ ๋ชจ๋ ํฌํจ๋ฉ๋๋ค 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"]
๋ณด์๋ค์ํผ, ๋ง์ง๋ง ๋ฌธ์์๋ง ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ์๊ฐ ํฌํจ๋์ด ์์ง ์์ผ๋ฏ๋ก contains(_:)
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ๋ ์์๋๋ก ์๋ํฉ๋๋ค. ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ์๋ฅผ ํฌํจํ๋ ์ด๋ชจํฐ์ฝ๊ณผ ๋น๊ตํ์ง ์๊ธฐ ๋๋ฌธ์์ด ๋ฐฉ๋ฒ์ ๋ง์ง๋ง ๋ฌธ์ ์ด์ธ์ ์ผ์น ํญ๋ชฉ์ ์ฐพ์ง ์์ต๋๋ค.
์ด๋ฅผ ํ์ฅํ๊ธฐ ์ํด String
๋๋น๊ฐ 0 ์ธ ์กฐ์ด๋๋ก ๋๋๋ ์ด๋ชจํฐ์ฝ ๋ฌธ์๋ก ๊ตฌ์ฑ๋๋ฅผ ๋ง๋ค๊ณ contains(_:)
๋ฉ์๋์ ์ ๋ฌํ๋ฉด๋ก ํ๊ฐ๋ฉ๋๋ค false
. ์ด ํจ๊ป ํ ์์๋ค contains(_:)
์ผ๋ก ๋์ผํ๋๋ range(of:) != nil
, ์ง์ ๋ ์ธ์์ ์ ํํ ์ผ์น๋ฅผ ์ฐพ์ผ๋ ค๊ณ ์๋ํ๋ค. ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ ์๋ก ๋๋๋ ๋ฌธ์๋ ๋ถ์์ ํ ์ํ์ค๋ฅผ ํ์ฑํ๊ธฐ ๋๋ฌธ์์ด ๋ฉ์๋๋ ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ ์๋ก ๋๋๋ ๋ฌธ์๋ฅผ ์์ ํ ์ํ์ค๋ก ๊ฒฐํฉํ๋ ๋์ ์ธ์์ ์ผ์นํ๋ ํญ๋ชฉ์ ์ฐพ์ผ๋ ค๊ณ ์๋ํฉ๋๋ค. ์ฆ, ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ ๋ฉ์๋๊ฐ ์ผ์นํ๋ ๊ฒ์ ์ฐพ์ง ์์ต๋๋ค.
- ์ธ์๋ ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ ์๋ก ๋๋ฉ๋๋ค.
- ๊ตฌ๋ฌธ ๋ถ์ ํ ๋ฌธ์์ด์ ๋ถ์์ ํ ์ํ์ค๊ฐ โโํฌํจ๋์ด ์์ง ์์ต๋๋ค (์ : ํญ์ด 0 ์ธ ์กฐ์ธ์ผ๋ก ๋๋๊ณ ํธํ๋๋ ๋ฌธ์๊ฐ ์๋).
์์ฐํ๋ ค๋ฉด :
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์ ํ์ฉํ์ง ๋ํ.
์ฌ๊ธฐ์ Foundation
๋ฉ์๋๋ฅผ ์ค๋ฒ๋ก๋ ํ์ผ๋ฏ๋ก ์๋ ๋ฉ์๋๊ฐ ํ์ํ๋ฉด ์ด๋ฆ์ ๋ฐ๊พธ์ญ์์ค.
extension String {
func contains(_ string: String) -> Bool {
return self.range(of: string, options: String.CompareOptions.literal) != nil
}
}
์ด์ ์ด ๋ฐฉ๋ฒ์ ๋ถ์์ ํ ์ํ์ค์์๋ ๊ฐ ๋ฌธ์์ “์ ๋๋ก”์๋ํฉ๋๋ค.
s.contains("๐ฉ") // true
s.contains("๐ฉ\u{200d}") // true
s.contains("\u{200d}") // true
๋ต๋ณ
์ฒซ ๋ฒ์งธ ๋ฌธ์ ๋ contains
(Swift ‘s String
is not with)๋ฅผ ์ฌ์ฉํ์ฌ Foundation์ ๋ธ๋ฆฌ์งํ๋ ๊ฒ Collection
์
๋๋ค.์ด NSString
๋์์ Swift๋งํผ ๊ฐ๋ ฅํ๊ฒ Emoji๋ก ๊ตฌ์ฑ๋ ํธ๋ค์ ๋ฏฟ์ง ์์ต๋๋ค. ์ฆ, Swift๋ ํ์ฌ ์ ๋ ์ฝ๋ 8์ ๊ตฌํํ๊ณ ์์ผ๋ฉฐ ์ ๋ ์ฝ๋ 10 ์์์ด ์ํฉ์ ๋ํ ์์ ์ด ํ์ํ๋ค๊ณ ์๊ฐํฉ๋๋ค (๋ฐ๋ผ์ ์ ๋ ์ฝ๋ 10์ ๊ตฌํํ๋ฉด ๋ชจ๋ ๊ฒ์ด ๋ฐ๋ ์ ์์ต๋๋ค.
์ผ์ ๋จ์ํํ๊ธฐ ์ํด Foundation์ ์ ๊ฑฐํ๊ณ ๋ณด๋ค ๋ช ํํ ๋ทฐ๋ฅผ ์ ๊ณตํ๋ Swift๋ฅผ ์ฌ์ฉํ์ญ์์ค. ์ฐ๋ฆฌ๋ ๋ฌธ์๋ก ์์ํ ๊ฒ์ ๋๋ค :
"๐ฉโ๐ฉโ๐งโ๐ฆ".characters.forEach { print($0) }
๐ฉโ
๐ฉโ
๐งโ
๐ฆ
ํ์ธ. ๊ทธ๊ฒ์ด ์ฐ๋ฆฌ๊ฐ ๊ธฐ๋ ํ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ ๊ฑฐ์ง๋ง์ ๋๋ค. ๊ทธ ์บ๋ฆญํฐ๋ค์ด ์ค์ ๋ก ๋ฌด์์ธ์ง ๋ด ์๋ค.
"๐ฉโ๐ฉโ๐งโ๐ฆ".characters.forEach { print(String($0).unicodeScalars.map{$0}) }
["\u{0001F469}", "\u{200D}"]
["\u{0001F469}", "\u{200D}"]
["\u{0001F467}", "\u{200D}"]
["\u{0001F466}"]
์โฆ ๊ทธ๋์ ["๐ฉZWJ", "๐ฉZWJ", "๐งZWJ", "๐ฆ"]
. ๊ทธ๊ฒ์ ๋ชจ๋ ๊ฒ์ ์กฐ๊ธ ๋ ๋ช
ํํ๊ฒ ๋ง๋ญ๋๋ค. ๐ฉ์ (๋)์ด ๋ชฉ๋ก์ ํ์์ด ์๋๋๋ค ( “๐ฉZWJ”).
๋ฌธ์ ๋ Character
“grapheme cluster”์ธ๋ฐ, ์ด๋ ZWJ๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ๊ณผ ๊ฐ์ด ์ฌ๋ฌผ์ ํจ๊ป ๊ตฌ์ฑํฉ๋๋ค. ์ค์ ๋ก ๊ฒ์ํ๋ ๊ฒ์ ์ ๋ ์ฝ๋ ์ค์นผ๋ผ์
๋๋ค. ๊ทธ๋ฆฌ๊ณ ๊ทธ๊ฒ์ ๋น์ ์ด ๊ธฐ๋ํ๋๋๋ก ์ ํํ๊ฒ ์๋ํฉ๋๋ค :
"๐ฉโ๐ฉโ๐งโ๐ฆ".unicodeScalars.contains("๐ฉ") // true
"๐ฉโ๐ฉโ๐งโ๐ฆ".unicodeScalars.contains("\u{200D}") // true
"๐ฉโ๐ฉโ๐งโ๐ฆ".unicodeScalars.contains("๐ง") // true
"๐ฉโ๐ฉโ๐งโ๐ฆ".unicodeScalars.contains("๐ฆ") // true
๋ฌผ๋ก ์ค์ ์กด์ฌํ๋ ์บ๋ฆญํฐ๋ ์ฐพ์ ์ ์์ต๋๋ค.
"๐ฉโ๐ฉโ๐งโ๐ฆ".characters.contains("๐ฉ\u{200D}") // true
(์ด๊ฒ์ Ben Leggiero์ ์์ ์ ํฌ๊ฒ ๋ณต์ ํ ๊ฒ์ ๋๋ค. ๋๋ ๊ทธ๊ฐ ๋๋ตํ๋ค๋ ๊ฒ์ ์๊ธฐ ์ ์ ์ด๊ฒ์ ๊ฒ์ํ์ต๋๋ค. ๋๊ตฐ๊ฐ์๊ฒ ๋ ๋ถ๋ช ํ ๊ฒฝ์ฐ๋ฅผ ๋๋นํ์ฌ ๋ ๋์ญ์์ค.)
๋ต๋ณ
Swift๋ a ZWJ
๊ฐ ๋ฐ๋ก ์์์๋ ๋ฌธ์๋ฅผ ๊ฐ์ง ํ์ฅ ๋ grapheme ํด๋ฌ์คํฐ๋ก ๊ฐ์ฃผ ํฉ๋๋ค. ๋ฌธ์ ๋ฐฐ์ด์ ๋ค์์ ๋งคํ ํ ๋ ์ด๊ฒ์ ๋ณผ ์ ์์ต๋๋ค unicodeScalars
.
Array(manual.characters).map { $0.description.unicodeScalars }
LLDB์์ ๋ค์์ ์ธ์ํฉ๋๋ค.
โฟ 4 elements
โฟ 0 : StringUnicodeScalarView("๐ฉโ")
- 0 : "\u{0001F469}"
- 1 : "\u{200D}"
โฟ 1 : StringUnicodeScalarView("๐ฉโ")
- 0 : "\u{0001F469}"
- 1 : "\u{200D}"
โฟ 2 : StringUnicodeScalarView("๐งโ")
- 0 : "\u{0001F467}"
- 1 : "\u{200D}"
โฟ 3 : StringUnicodeScalarView("๐ฆ")
- 0 : "\u{0001F466}"
๋ํ .contains
ํ์ฅ ๋ grapheme ํด๋ฌ์คํฐ๋ฅผ ๋จ์ผ ๋ฌธ์๋ก ๊ทธ๋ฃนํํฉ๋๋ค. ์๋ฅผ ๋ค์ด, ํ๊ธ ๋ฌธ์ แ
, แ
ก
๋ฐ แซ
(์ ์ฌ์ฉํ์ฌ ํ๊ตญ์ด ๋จ์ด “one”:)์ ์์ฑํ๋ ๊ฒฝ์ฐ แแ
กแซ
:
"\u{1112}\u{1161}\u{11AB}".contains("\u{1112}") // false
แ
์ธ ๊ฐ์ ์ฝ๋ ํฌ์ธํธ๊ฐ ํ๋์ ๋ฌธ์๋ก ์๋ํ๋ ํ๋์ ํด๋ฌ์คํฐ๋ก ๊ทธ๋ฃนํ๋์ด ์๊ธฐ ๋๋ฌธ์์ด๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค . ๋ง์ฐฌ๊ฐ์ง๋ก \u{1F469}\u{200D}
( WOMAN
ZWJ
)๋ ํ๋์ ๋ฌธ์๋ก ์๋ํ๋ ํ๋์ ํด๋ฌ์คํฐ์
๋๋ค.
๋ต๋ณ
๋ค๋ฅธ ๋ต๋ณ์ Swift์ ๊ธฐ๋ฅ์ ๋ํด ์ค๋ช ํ์ง๋ง ๊ทธ ์ด์ ์ ๋ํด์๋ ์์ธํ ๋ค๋ฃจ์ง ์์ต๋๋ค.
โร โ๊ฐโร โ๊ณผ ๊ฐ์ ๊ฒ์ผ๋ก ์์ํ์ญ๋๊น? ๋๋ ๋น์ ์ด ๊ธฐ๋ํฉ๋๋ค.
์ด ์ค ํ๋๋ ๊ฒฐํฉ๊ธฐ๊ฐ์๋ ๋ฌธ์์ด๊ณ ๋ค๋ฅธ ํ๋๋ ๋จ์ผ ๋ฌธ์๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ๊ธฐ๋ณธ ์บ๋ฆญํฐ์ ์ฌ๋ฌ ๊ฐ์ง ์กฐํฉ์ ์ถ๊ฐ ํ ์ ์์ผ๋ฉฐ ์ธ๊ฐ์ ์ฌ์ ํ โโ๋จ์ผ ์บ๋ฆญํฐ๋ก ๊ฐ์ฃผํฉ๋๋ค. ์ด๋ฌํ ์ข ๋ฅ์ ๋ถ์ผ์น๋ฅผ ์ฒ๋ฆฌํ๊ธฐ ์ํด, ์ฌ์ฉ ๋ ์ฝ๋ ํฌ์ธํธ์ ๊ด๊ณ์์ด ์ธ๊ฐ์ด ์บ๋ฆญํฐ๋ฅผ ๊ณ ๋ คํ ๊ฒ์ ๋ํ๋ด๋ ๊ทธ๋ ํ ๊ฐ๋ ์ด ๋ง๋ค์ด์ก์ต๋๋ค.
์ด์ ๋ฌธ์ ๋ฉ์์ง ์๋น์ค๋ ๋ช ๋
๋์ ๋ฌธ์๋ฅผ ๊ทธ๋ํฝ ์ด๋ชจํฐ์ฝ์ผ๋ก ๊ฒฐํฉ ํด ์์ต๋๋ค :)
โ ๐
. ๊ทธ๋์ ๋ค์ํ ์ด๋ชจํฐ์ฝ์ด ์ ๋ ์ฝ๋์ ์ถ๊ฐ๋์์ต๋๋ค.
์ด ์๋น์ค๋ ๋ํ ์ด๋ชจํฐ์ฝ์ ํฉ์ฑ ์ด๋ชจํฐ์ฝ์ผ๋ก ๊ฒฐํฉํ๊ธฐ ์์ํ์ต๋๋ค.
๋ฌผ๋ก ๊ฐ๋ฅํ ๋ชจ๋ ์กฐํฉ์ ๊ฐ๋ณ ์ฝ๋ ํฌ์ธํธ๋ก ์ธ์ฝ๋ฉํ๋ ํฉ๋ฆฌ์ ์ธ ๋ฐฉ๋ฒ์ ์์ผ๋ฏ๋ก ์ ๋ ์ฝ๋ ์ปจ์์์์ ์ด๋ฌํ ๋ณตํฉ ๋ฌธ์๋ฅผ ํฌํจํ๋๋ก ๊ทธ๋ ํ ๊ฐ๋
์ ํ์ฅํ๊ธฐ๋ก ๊ฒฐ์ ํ์ต๋๋ค.
์ด๊ฒ์ด๋์ด ๊ท๊ฒฐ "๐ฉโ๐ฉโ๐งโ๐ฆ"
์ค์ํํธ๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ํ ๋๋๋ก ์์ ์์ค์์ ํจ๊ป ์๋ํ๋๋กํ๋ ค๋ ๊ฒฝ์ฐ ํ๋์ “๊ทธ๋ ํ ํด๋ฌ์คํฐ”๋ก ๊ณ ๋ ค๋์ด์ผํ๋ค.
"๐ฆ"
๊ทธ ์ผ๋ถ๋ก ํฌํจ๋์ด ์๋์ง ํ์ธํ๋ ค๋ฉด ๋ ๋ฎ์ ์์ค์ผ๋ก ๋ด๋ ค ๊ฐ์ผํฉ๋๋ค.
Swift ๊ตฌ๋ฌธ์ ๋ชจ๋ฅด๋ฏ๋ก ์ฌ๊ธฐ์ ๋น์ทํ ์์ค์ ์ ๋ ์ฝ๋ ์ง์ ํ 6์ด ์์ต๋๋ค.
(Perl 6์ ์ ๋ ์ฝ๋ ๋ฒ์ 9๋ฅผ ์ง์ํ๋ฏ๋ก ๋ถ์ผ์น๊ฐ์์ ์ ์์ต๋๋ค)
say "\c[family: woman woman girl boy]" eq "๐ฉโ๐ฉโ๐งโ๐ฆ"; # True
# .contains is a Str method only, in Perl 6
say "๐ฉโ๐ฉโ๐งโ๐ฆ".contains("๐ฉโ๐ฉโ๐งโ๐ฆ") # True
say "๐ฉโ๐ฉโ๐งโ๐ฆ".contains("๐ฆ"); # False
say "๐ฉโ๐ฉโ๐งโ๐ฆ".contains("\x[200D]"); # False
# comb with no arguments splits a Str into graphemes
my @graphemes = "๐ฉโ๐ฉโ๐งโ๐ฆ".comb;
say @graphemes.elems; # 1
๋ ๋ฒจ์ ๋ด๋ ค ๊ฐ์
# look at it as a list of NFC codepoints
my @components := "๐ฉโ๐ฉโ๐งโ๐ฆ".NFC;
say @components.elems; # 7
say @components.grep("๐ฆ".ord).Bool; # True
say @components.grep("\x[200D]".ord).Bool; # True
say @components.grep(0x200D).Bool; # True
์ด ์์ค์ผ๋ก ๋ด๋ ค ๊ฐ๋ฉด ์ผ๋ถ ์์ ์ด ๋ ์ด๋ ค์ ์ง ์ ์์ต๋๋ค.
my @match = "๐ฉโ๐ฉโ๐งโ๐ฆ".ords;
my $l = @match.elems;
say @components.rotor( $l => 1-$l ).grep(@match).Bool; # True
.contains
Swift์์ ๋ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค๊ณ ๊ฐ์ ํ์ง๋ง ๋ ์ด๋ ค์ด ๋ค๋ฅธ ๊ฒ์ด ์๋ค๋ ๊ฒ์ ์๋ฏธํ์ง๋ ์์ต๋๋ค.
์ด ๋ ๋ฒจ์์ ์์ ํ๋ฉด ์๋ฅผ ๋ค์ด ๋ณตํฉ ๋ฌธ์์ ์ค๊ฐ์ ์ค์๋ก ๋ฌธ์์ด์ ๋ถ๋ฆฌํ๊ธฐ๊ฐ ํจ์ฌ ์ฝ์ต๋๋ค.
๋ถ์ฃผ์ํ๊ฒ ์๊ตฌํ๋ ๊ฒ์์ด ๋์ ์์ค์ ํํ์ด ๋ฎ์ ์์ค์ ํํ์ฒ๋ผ ์๋ํ์ง ์๋ ์ด์ ์ ๋๋ค. ๋๋ต์ ๋ฌผ๋ก ์๋๋๋ค.
โ ์ ์ด๊ฒ์ด ๊ทธ๋ ๊ฒ ๋ณต์กํด์ผํฉ๋๊น? โ๋ผ๊ณ ์ค์ค๋ก์๊ฒ ๋ฌผ์ผ๋ฉด ๋๋ต์ ๋ฌผ๋ก โ ์ธ๊ฐ โ์ ๋๋ค.
๋ต๋ณ
์ค์ํํธ 4.0 ์ ๋ฐ์ดํธ
SE-0163์ ๋ฌธ์ํ ๋ ๋ฐ์ ๊ฐ์ด, Swift 4 ์ ๋ฐ์ดํธ์์ String์ ๋ง์ ์์ ์ ๋ฐ์์ต๋๋ค . ์ด ๋ฐ๋ชจ์๋ ๋ ๊ฐ์ ๋ค๋ฅธ ๊ตฌ์กฐ๋ฅผ ๋ํ๋ด๋ ๋ ๊ฐ์ ์ด๋ชจ์ง๊ฐ ์ฌ์ฉ๋ฉ๋๋ค. ๋ ๋ค ์ผ๋ จ์ ์ด๋ชจํฐ์ฝ๊ณผ ๊ฒฐํฉ๋ฉ๋๋ค.
๐๐ฝ
๋ ์ด๋ชจํฐ์ฝ์ ์กฐํฉ ๐
์ด๋ฉฐ๐ฝ
๐ฉโ๐ฉโ๐งโ๐ฆ
๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ์๊ฐ ์ฐ๊ฒฐ๋ 4 ๊ฐ์ ์ด๋ชจ ์ง ์กฐํฉ์
๋๋ค. ํ์์๐ฉโjoiner๐ฉโjoiner๐งโjoiner๐ฆ
1. ์นด์ดํธ
Swift 4.0์์ ๊ทธ๋ฆผ ์ด๋ชจํฐ์ฝ์ grapheme ํด๋ฌ์คํฐ๋ก ๊ณ์ฐ๋ฉ๋๋ค. ๋ชจ๋ ๋จ์ผ ์ด๋ชจ์ง๋ 1๋ก ๊ณ์ฐ๋ฉ๋๋ค.์ด count
์์ฑ์ ๋ฌธ์์ด์๋ ์ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ์ง์ ํธ์ถ ํ ์ ์์ต๋๋ค.
"๐๐ฝ".count // 1. Not available on swift 3
"๐ฉโ๐ฉโ๐งโ๐ฆ".count // 1. Not available on swift 3
๋ฌธ์์ด์ ๋ฌธ์ ๋ฐฐ์ด๋ Swift 4.0์์ grapheme ํด๋ฌ์คํฐ๋ก ๊ณ์ฐ๋๋ฏ๋ก ๋ค์ ๋ ์ฝ๋๊ฐ ๋ชจ๋ 1๋ก ์ธ์๋ฉ๋๋ค.์ด ๋ ๊ฐ์ ์ด๋ชจ์ง๋ ์ด๋ชจ ์ง ์ํ์ค์ ์์ด๋ฉฐ ์ฌ๋ฌ ์ด๋ชจ์ง๊ฐ ๋๋น๊ฐ 0 ์ธ ์กฐ์ด ๋ํฐ์ ํจ๊ป โโ๋๋์์ด ๊ฒฐํฉ \u{200d}
๋ฉ๋๋ค. swift 3.0์์ ์ด๋ฌํ ๋ฌธ์์ด์ ๋ฌธ์ ๋ฐฐ์ด์ ๊ฐ ์ด๋ชจ์ง๋ฅผ ๋ถ๋ฆฌํ๊ณ ์ฌ๋ฌ ์์๊ฐ์๋ ๋ฐฐ์ด (์ด๋ชจ ์ง)์ ์์ฑํฉ๋๋ค. ์ด ํ๋ก์ธ์ค์์๋ ๊ฒฐํฉ์๊ฐ ๋ฌด์๋ฉ๋๋ค. ๊ทธ๋ฌ๋ Swift 4.0์์ ๋ฌธ์ ๋ฐฐ์ด์ ๋ชจ๋ ์ด๋ชจํฐ์ฝ์ ํ ์กฐ๊ฐ์ผ๋ก ๊ฐ์ฃผํฉ๋๋ค. ๋ฐ๋ผ์ ๋ชจ๋ ์ด๋ชจ ์ง์ ์ด๋ชจํฐ์ฝ์ ํญ์ 1์
๋๋ค.
"๐๐ฝ".characters.count // 1. In swift 3, this prints 2
"๐ฉโ๐ฉโ๐งโ๐ฆ".characters.count // 1. In swift 3, this prints 4
unicodeScalars
Swift 4์์๋ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค. ์ฃผ์ด์ง ๋ฌธ์์ด์์ ๊ณ ์ ํ ์ ๋ ์ฝ๋ ๋ฌธ์๋ฅผ ์ ๊ณตํฉ๋๋ค.
"๐๐ฝ".unicodeScalars.count // 2. Combination of two emoji
"๐ฉโ๐ฉโ๐งโ๐ฆ".unicodeScalars.count // 7. Combination of four emoji with joiner between them
2. ํฌํจ
Swift 4.0 contains
์์์ด ๋ฐฉ๋ฒ์ ์ด๋ชจํฐ์ฝ์์ ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ์๋ฅผ ๋ฌด์ํฉ๋๋ค. ๋ฐ๋ผ์์ 4 ๊ฐ ์ด๋ชจํฐ์ฝ ๊ตฌ์ฑ ์์ ์ค ํ๋์ ๋ํด true๋ฅผ ๋ฐํ "๐ฉโ๐ฉโ๐งโ๐ฆ"
ํ๊ณ ๊ฒฐํฉ์๋ฅผ ํ์ธํ๋ฉด false๋ฅผ ๋ฐํํฉ๋๋ค. ๊ทธ๋ฌ๋ Swift 3.0์์๋ ๊ฒฐํฉ์๊ฐ ๋ฌด์๋์ง ์๊ณ ์์ ๊ทธ๋ฆผ ์ด๋ชจํฐ์ฝ๊ณผ ๊ฒฐํฉ๋ฉ๋๋ค. ๋ฐ๋ผ์ "๐ฉโ๐ฉโ๐งโ๐ฆ"
์ฒ์ ์ธ ๊ฐ์ง ๊ตฌ์ฑ ์์ ์ด๋ชจํฐ์ฝ์ด ํฌํจ๋์ด ์๋์ง ํ์ธํ๋ฉด ๊ฒฐ๊ณผ๊ฐ ๊ฑฐ์ง์ด๋ฉ๋๋ค.
"๐๐ฝ".contains("๐") // true
"๐๐ฝ".contains("๐ฝ") // true
"๐ฉโ๐ฉโ๐งโ๐ฆ".contains("๐ฉโ๐ฉโ๐งโ๐ฆ") // true
"๐ฉโ๐ฉโ๐งโ๐ฆ".contains("๐ฉ") // true. In swift 3, this prints false
"๐ฉโ๐ฉโ๐งโ๐ฆ".contains("\u{200D}") // false
"๐ฉโ๐ฉโ๐งโ๐ฆ".contains("๐ง") // true. In swift 3, this prints false
"๐ฉโ๐ฉโ๐งโ๐ฆ".contains("๐ฆ") // true
๋ต๋ณ
์ ๋ ์ฝ๋ ํ์ค๊ณผ ๋งค์ฐ ์ ์ฌํ ์ด๋ชจ์ง๋ ๋ฏฟ์ ์ ์์ ์ ๋๋ก ๋ณต์กํฉ๋๋ค. ํผ๋ถ์, ์ฑ๋ณ, ์์ , ์ฌ๋ ๊ทธ๋ฃน, ๋๋น๊ฐ 0 ์ธ ๊ฒฐํฉ ์ ์ํ์ค, ํ๋๊ทธ (2 ๋ฌธ์ ์ ๋ ์ฝ๋) ๋ฐ ๊ธฐํ ๋ณต์กํ ๋ฌธ์ ๋ก ์ธํด ์ด๋ชจํฐ์ฝ ํ์ฑ์ด ์ง์ ๋ถํด์ง ์ ์์ต๋๋ค. ํฌ๋ฆฌ์ค๋ง์ค ํธ๋ฆฌ, ํผ์ ์กฐ๊ฐ ๋๋ ๋๋ฏธ ๋๋ฏธ๋ ๋ชจ๋ ๋จ์ผ ์ ๋ ์ฝ๋ ์ฝ๋ ํฌ์ธํธ๋ก ํํํ ์ ์์ต๋๋ค. ์๋ก์ด ์ด๋ชจ์ง๊ฐ ์๊ฐ ๋ ๋ iOS ์ง์๊ณผ ์ด๋ชจ ์ง ๋ฆด๋ฆฌ์ค ์ฌ์ด์ ์ง์ฐ์ด ์๋ค๋ ๊ฒ์ ๋งํ ๊ฒ๋ ์์ต๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ๋ฒ์ ์ iOS๋ ๋ค๋ฅธ ๋ฒ์ ์ ์ ๋ ์ฝ๋ ํ์ค์ ์ง์ํฉ๋๋ค.
TL; DR. ๋๋์ด ๊ธฐ๋ฅ์ ์ฐ๊ตฌํ๊ณ ๋ฌธ์์ด์ ์ด๋ชจํฐ์ฝ์ผ๋ก ๊ตฌ๋ฌธ ๋ถ์ํ๋ ๋ฐ ๋์์ด ๋๋ JKEmoji ์ ์ ์ ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ณตํ์ต๋๋ค . ๋ค์๊ณผ ๊ฐ์ด ์ฝ๊ฒ ๊ตฌ๋ฌธ ๋ถ์ํฉ๋๋ค.
print("I love these emojis ๐ฉโ๐ฉโ๐งโ๐ฆ๐ช๐พ๐งฅ๐ง๐ฟ๐".emojiCount)
5
์ต์ ์ ๋ ์ฝ๋ ๋ฒ์ ( ์ต๊ทผ 12.0) ์ผ๋ก ์ธ์ ๋ ๋ชจ๋ ์ด๋ชจ ์ง์ ๋ก์ปฌ ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ ๊ธฐ์ ์ผ๋ก ์๋ก ๊ณ ์น๊ณ ์คํ์ค์ธ OS ๋ฒ์ ์์ ์ ํจํ ์ด๋ชจ ์ง๋ก ์ธ์๋๋ ๊ฒ๊ณผ ๋ก์ปฌ ์ฐธ์กฐ๋ฅผ ๋นํธ ๋งต์ผ๋ก ํ์ํ์ฌ ์ํธ ์ฐธ์กฐํฉ๋๋ค. ์ธ์ ํ ์์๋ ์ด๋ชจํฐ์ฝ ๋ฌธ์
๋ ธํธ
๋ด๊ฐ ์ ์์์ ๋ช ํํ๊ฒ ๋ฐํ์ง ์๊ณ ๋ด ๋์๊ด ๊ด๊ณ ์ ๋ํ ์ด์ ๋ต๋ณ์ด ์ญ์ ๋์์ต๋๋ค. ๋๋ ์ด๊ฒ์ ๋ค์ ์ธ์ ํ๊ณ ์๋ค.