[swift] ์™œ ๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ๊ณผ ๊ฐ™์€ ์ด๋ชจ ์ง€ ๋ฌธ์ž๊ฐ€ 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๋Š” ์ž์ฒด (์ข‹์€)์™€ ์†Œ๋…„ (์ข‹์€!)์„ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค๊ณ  ๋งํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๊ทธ๊ฒƒ์€ ์—ฌ์ž, ์—ฌ์ž ๋˜๋Š” ํญ์ด 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!.



๋‹ต๋ณ€

์ด๊ฒƒ์€ StringSwift ์—์„œ ์œ ํ˜•์ด ์ž‘๋™ํ•˜๋Š” 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 ์ธ ๊ฒฐํ•ฉ ์ž๋กœ ๋๋‚˜๋Š” ๋ฌธ์ž๋ฅผ ์™„์ „ํ•œ ์‹œํ€€์Šค๋กœ ๊ฒฐํ•ฉํ•˜๋Š” ๋™์•ˆ ์ธ์ˆ˜์™€ ์ผ์น˜ํ•˜๋Š” ํ•ญ๋ชฉ์„ ์ฐพ์œผ๋ ค๊ณ  ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๊ฒฝ์šฐ ๋ฉ”์†Œ๋“œ๊ฐ€ ์ผ์น˜ํ•˜๋Š” ๊ฒƒ์„ ์ฐพ์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

  1. ์ธ์ˆ˜๋Š” ๋„ˆ๋น„๊ฐ€ 0 ์ธ ๊ฒฐํ•ฉ ์ž๋กœ ๋๋‚ฉ๋‹ˆ๋‹ค.
  2. ๊ตฌ๋ฌธ ๋ถ„์„ ํ•  ๋ฌธ์ž์—ด์— ๋ถˆ์™„์ „ํ•œ ์‹œํ€€์Šค๊ฐ€ โ€‹โ€‹ํฌํ•จ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค (์˜ˆ : ํญ์ด 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 Stringis 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

.containsSwift์—์„œ ๋” ์‰ฝ๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค๊ณ  ๊ฐ€์ • ํ•˜์ง€๋งŒ ๋” ์–ด๋ ค์šด ๋‹ค๋ฅธ ๊ฒƒ์ด ์—†๋‹ค๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.

์ด ๋ ˆ๋ฒจ์—์„œ ์ž‘์—…ํ•˜๋ฉด ์˜ˆ๋ฅผ ๋“ค์–ด ๋ณตํ•ฉ ๋ฌธ์ž์˜ ์ค‘๊ฐ„์— ์‹ค์ˆ˜๋กœ ๋ฌธ์ž์—ด์„ ๋ถ„๋ฆฌํ•˜๊ธฐ๊ฐ€ ํ›จ์”ฌ ์‰ฝ์Šต๋‹ˆ๋‹ค.


๋ถ€์ฃผ์˜ํ•˜๊ฒŒ ์š”๊ตฌํ•˜๋Š” ๊ฒƒ์€์ด ๋†’์€ ์ˆ˜์ค€์˜ ํ‘œํ˜„์ด ๋‚ฎ์€ ์ˆ˜์ค€์˜ ํ‘œํ˜„์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ์ด์œ ์ž…๋‹ˆ๋‹ค. ๋Œ€๋‹ต์€ ๋ฌผ๋ก  ์•„๋‹™๋‹ˆ๋‹ค.

โ€œ ์™œ ์ด๊ฒƒ์ด ๊ทธ๋ ‡๊ฒŒ ๋ณต์žกํ•ด์•ผํ•ฉ๋‹ˆ๊นŒ? โ€๋ผ๊ณ  ์Šค์Šค๋กœ์—๊ฒŒ ๋ฌผ์œผ๋ฉด ๋Œ€๋‹ต์€ ๋ฌผ๋ก โ€œ ์ธ๊ฐ„ โ€์ž…๋‹ˆ๋‹ค.


๋‹ต๋ณ€

์Šค์œ„ํ”„ํŠธ 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 ๋ฒ„์ „์—์„œ ์œ ํšจํ•œ ์ด๋ชจ ์ง€๋กœ ์ธ์‹๋˜๋Š” ๊ฒƒ๊ณผ ๋กœ์ปฌ ์ฐธ์กฐ๋ฅผ ๋น„ํŠธ ๋งต์œผ๋กœ ํ‘œ์‹œํ•˜์—ฌ ์ƒํ˜ธ ์ฐธ์กฐํ•ฉ๋‹ˆ๋‹ค. ์ธ์‹ ํ•  ์ˆ˜์—†๋Š” ์ด๋ชจํ‹ฐ์ฝ˜ ๋ฌธ์ž

๋…ธํŠธ

๋‚ด๊ฐ€ ์ €์ž์ž„์„ ๋ช…ํ™•ํ•˜๊ฒŒ ๋ฐํžˆ์ง€ ์•Š๊ณ  ๋‚ด ๋„์„œ๊ด€ ๊ด‘๊ณ ์— ๋Œ€ํ•œ ์ด์ „ ๋‹ต๋ณ€์ด ์‚ญ์ œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ์ด๊ฒƒ์„ ๋‹ค์‹œ ์ธ์ •ํ•˜๊ณ ์žˆ๋‹ค.


๋‹ต๋ณ€