1. [ A String as a collection of a Grapheme cluster or clusters ] : ์ค์ํํธ์๊ฒ ์คํธ๋ง์ ๊ทธ๋ํ ํด๋ฌ์คํฐ์ ์ปฌ๋ ์ ์ด๋ค.
let cafeNormal = "café"
let cafeCombined = "cafe\u{0301}"
// \u0000001100000001 => hexacoded unicode code point
์๋ฅผ ๋ค์ด, ์์์ ์ฒซ ๋ฒ์งธ ์์์ ๋ ๋ฒ์งธ ์์ ์ ๋์ฝ๋ ์ ์ฝ๋ ํฌ์ธํธ ๊ฐฏ์๋ 4๊ฐ์ 5๊ฐ๋ก ์์ดํ๋, ์ค์ํํธ๋ ์์ด ์ํ๋ฒณ e์ ๋ฐ์๊ธฐํธ๋ฅผ ํด๋ฌ์คํฐ๋ก ๋ฌถ์ด ํ๋์ ์บ๋ฆญํฐ๋ก ๋ด ๋๋ค.
// ๊ทธ๋ ๊ธฐ ๋๋ฌธ์
cafeNormal.unicodeScalars.count // 4
cafeCombining.unicodeScalars.count // 5
cafeNormal.count // 4
cafeCombining.count // 4
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ฝ๋ํฌ์ธํธ๋ ์๋ก ๋ฌ๋ผ๋, char์ ๋ํ ์นด์ดํธ๋ ์๋ก ๊ฐ๊ฒ ๋ฉ๋๋ค.
2. [ Indexed String ] ์คํธ๋ง ์๋ฃํ์ ์ธ๋ฑ์ฑํ๊ธฐ -> ์คํธ๋ง[3] ์ด๊ฑฐ ์๋จ ...
์์์ ๋ดค๋ฏ์ด, ๋ ๊ฐ์ ์ฝ๋ ํฌ์ธํธ๊ฐ ๋ฌถ์ฌ์ ํ๋์ ์บ๋ฆญํฐ๊ฐ ๋๊ธฐ๋ ํ๋ ๊ฒ์ ๋ณด์์๋ค ์ํผ, ์คํธ๋ง ๋ฐ์ดํฐ ํ์์ ๋ํ ์ธ๋ฑ์ฑ์
์๋ฌด๋ฆฌ ์ปฌ๋ ์ ์ด์ด๋ [2], [1]๊ณผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ํ ์ ์์ต๋๋ค. ํด๋ฌ์คํฐ๊ฐ ๋ ์ ์์ด, ๋ฉ๋ชจ๋ฆฌ ์ด๋๋ ์ค ๋ ๊ฐ๊ฐ ๋ฌถ์ฌ ํ๋์ ์บ๋ฆญํฐ๋ฅผ ๋ํ๋ผ ์๋ ์๊ธฐ ๋๋ฌธ์ด์ฃ . ์ ์ด๋ ์ค์ํํธ์์๋ ๊ทธ๋ฌ๋๊น, ์ฐ๋ฆฌ๋ ๋ฐฐ์์ผํ ๋ฟ์ ๋๋ค.
๊ทธ๋ผ ์คํธ๋ง ์๋ฃํ์ ์ธ๋ฑ์ฑ์ ์ด๋ป๊ฒ ํ ๊น์?
๋น๋์ 1๋จ๊ณ. ์์ ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค, ๋ ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค
// 1). ์ฐ์ ์บ๋ฆญํฐ ์ปฌ๋ ์
์ธ ์คํธ๋ง์ ์ฒซ ๋ฒ์งธ ์์์ ์ธ๋ฑ์ค๋ฅผ ๊ตฌํด๋ด
์๋ค.
let firstIndex = cafeCombining.startIndex // ์ฒซ ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค
// 2). ์ด๋ ๊ฒ ์ค์ํํธ๊ฐ ๊ตฌํด๋์ ์ธ๋ฑ์ค๋ฅผ ์ธ๋ฑ์ฑ์ ์ฌ์ฉํฉ๋๋ค.
cafeCombining[firstIndex] // String์ ์ฒซ ๋ฒ์งธ "์บ๋ฆญํฐ" ์
๋๋ค.
// 3). ๊ทธ๋ ๋ค๋ฉด ๋ง์ง๋ง ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค๋? ๊ทธ๊ฒ๋ ์ค์ํํธ๊ฐ ์ฃผ๋ ๊ฑธ๋ก๋ง ํด์ !!
// *** warning : cafeCombining.endIndex๋ "1 char past the end of the string์ด๋ผ๊ณ ํ๋,
// .index(before: cafeCombining.endIndex) ์๋ ์ธ๋ฑ์ค๊ฐ ๋ฆฌํดํ ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค์ ํ ์บ๋ฆญํฐ ์ด์ ์
// ์ธ๋ฑ์ค๋ฅผ ํด์ผ ์ค์ง์ ์ผ๋ก ์คํธ๋ง์ ๋ง์ง๋ง ์บ๋ฆญํฐ๋ฅผ ๊ตฌํ ์ ์๋ค๊ณ ํ๋ค์."
let lastIndex = cafeCombining.index(before: cafeCombining.endIndex) // -> é
// 4). ๊ทธ๋ผ ๋ ์ด๊ฑธ ์ด์ฉํด์ ๋ง์ง๋ง ์บ๋ฆญํฐ๋ฅผ ๊ตฌํ์๋ฉด ๋ฉ๋๋ค.
cafeCombining[lastIndex]
๋น๋์ 2๋จ๊ณ. => .index( .startIndex, offsetBy: ~๋ฒ์งธ ๊ธ์(Int) ) ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ธฐ
// ์ฒซ ๋ฒ์งธ ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค๋ก ๋ถํฐ => 3 ์บ๋ฆญํฐ ๋จ์ด์ง ์บ๋ฆญํฐ์ ์ธ๋ฑ์ค
let fourthIndex = cafeCombining.index(cafeCombining.startIndex, offsetBy: 3)
// ์์์ .index(์ฒซ ์บ๋ฆญ ์ธ๋ฑ์ค, ์คํ์
: ์บ๋ฆญํฐ ์)๋ก ํ๊ฒน๊ฒ ๊ตฌํ ์ธ๋ฑ์ค๋ฅผ [ ] <- ์๋ธ์คํฌ๋ฆฝํธ์ ๋ฃ์ด์
// ๋ง์นจ๋ด, ๋ค ๋ฒ์งธ ์บ๋ฆญํฐ๋ฅผ ๊ตฌํฉ๋๋ค.
let fourthChar = cafeCombining[fourthIndex] // -> é
์ค์ํํธ๋ ํ ๋ง์ด ์๋ค๋ค์. " ๋๋ ํ ๋ง์ ์์ด, ์ด๋ ๊ฒ ๊ณ ์ํ๋ ์ด์ ๋ ๋ฐ๋ก ... "
์ ํ์ค๋ฝ๊ฒ๋, ๊ทธ๋ํ ์กฐํฉ์ ์ฐพ์ ํ ์บ๋ฆญํฐ๋ก ๋ฌถ์ด์ ๋๋๋งํ๋ ๊ฒ์ ํ ์บ๋ฆญํฐ์ ๋ค์์ฑ๊ณผ ์ปค์คํฐ๋ง์ด์ง ๊ฐ๋ฅ์ฑ์ ์ ๊ณตํฉ๋๋ค.
์ ๋ง, ์ ํ์ด ๋ง๋ ์ธ์ด๋ต๋ค์ ...
3. [ String equality ] ์คํธ๋ง์ ๋๋ฑ์ฑ ๋น๊ต
๊ทธ๋ ๋ค๋ฉด, ์ค์ํํธ ๋ง์ ์คํธ๋ง์ ๋๋ฑ์ฑ์ ๋น๊ตํ๋ ๋ฐฉ์์ ๋ฌด์์ผ๊น์?
์๋๋๋ค. ์ค์ํํธ์ ์คํธ๋ง ๋๋ฑ์ฑ ๋น๊ต์ ์์น์,
canonicalization์ ๋๋ค. ์ ๋ ๊ธฐ์ค์ผ๋ก ํ๊ฐ? ์ ๋น์ทํ ์๋ฏธ๋ก ๋ณผ ์ ์์์ง ๋ชจ๋ฅด๊ฒ ์ง๋ง,
1). ๊ฒฐ๊ตญ ๋ ๋ฆฝ๋ ์ฝ๋ํฌ์ธํธ๋ฅผ ์ชผ๊ฐ์ ๋ถํด๋ ๊ทธ๋ํ ์กฐํฉ์ผ๋ก ๋งตํํ๋์ง,
2). ๊ทธ๋ํ ์กฐํฉ์, ์กฐํฉํ์ฌ ํ๋์ ๋ ๋ฆฝ๋ ์ฝ๋ํฌ์ธํธ๋ก ๋งตํํ๋์ง,
๊ฒฐ๊ตญ์ "ํต์ผ๋" ๊ธฐ์ค์ผ๋ก ๋ ๊ฐ์ ์คํธ๋ง์ ๋น๊ตํฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด, ์ฝ๋ ํฌ์ธํธ ์กฐํฉ์ด ์๋ฌด๋ฆฌ ๋ค์ํ ์ ์์ด๋,
๊ฒฐ๊ตญ ์ฐ๋ฆฌ๊ฐ ๋ณผ ๋ ๊ฐ์ ์บ๋ฆญํฐ๋ค์ด ๊ฐ์ ์์๋ก ์๋ค๋ฉด( ์ด๊ฒ ๋ ์ค์ฉ์ ์ด์ฃ . ) ๊ฐ์ ์คํธ๋ง์ ๊ฒฐ๊ตญ ๊ฐ์ ์คํธ๋ง์ผ๋ก ํ๊ฐํฉ๋๋ค.
cafeCombining == cafeNormal -> true
์ด๋ ๊ฒ ๋๋ ๊ฒ์ด์ฃ . ์ด๊ฒ ์ค์ํํธ์ ๋ฐฉ์์ ๋๋ค.
4. String as bi-directional collections ์๋ฐฉํฅ์ฑ ์ปฌ๋ ์ ์ผ๋ก์์ ์ค์ํํธ ์คํธ๋ง
let name = "Matt"
let backwardsName = name.reversed( )
//
let secondCharIndex = backwardsName.index(backwardsName.startIndex, offsetBy: 1)
let secondChar = backwardsName[secondCharIndex]
//
let backwardNameString = String(backwardsName)
String => .reversed( )๋ฅผ ์ ์ฉํ๋ ๊ฒ์, char์ collection์ธ String์, ๋ง์น ๊ทธ๊ฒ์ ๊ฑฐ๊พธ๋กํ ์คํธ๋ง์ผ๋ก ์ธ ์ ์๊ฒ ๋ง๋ค์ด์ฃผ๋, a thin wrapper๋ฅผ ์์ ๋ค๊ณ ์๊ฐํ๋ฉด ๋ฉ๋๋ค. ํ์ง๋ง ์ฌ์ ํ ์ถ๊ฐ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฐ์ง ์์ผ๋ฉด์ ๊ทธ๋ ๊ฒ ์ฌ์ฉํฉ๋๋ค !! ๊ทธ๋ฌ๋ฉด 2๋ฒ์งธ ๋ธ๋ก์์ ๋ณด์ค ์ ์๋ค์ํผ, ๊ฑฐ๊พธ๋ก๋ ์คํธ๋ง์ธ ๊ฒ์ฒ๋ผ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ธ๋ฑ์ฑ ๊ฐ๋ฅํฉ๋๋ค. ๋ฉํ๋ ์คํธ๋ง์ ์คํธ๋ง ๋ฐ์ดํฐ ํ์์ผ๋ก ๋ง๋ค์ด๋ฒ๋ฆฌ๋ ค๋ฉด => String( ) : ์คํธ๋ง ํ์ ์ปจ๋ฒ์ ผ์ ์ฌ์ฉํ๋ฉด๋ฉ๋๋ค. ํ์ง๋ง, ๋ง์ฝ ๊ฑฐ๊พธ๋ก๋ ํํ์ ๋์์ ์ฒซ ๊ธ์์ ๊ฐ์ ๋ฐฉ์์ผ๋ก ์ด์ฉํ๋ ค๋ฉด, ๊ตณ์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ญ๋นํ๋ฉด์, ํ์ ์ปจ๋ฒ์ ผ์ ํ ํ์๊ฐ ์์ต๋๋ค.
5. Raw Strings ์์คํธ๋ง
์ค์ํํธ์์ ์คํธ๋ง์
1. " " " " ๊ฐ ๋์ค๋ฉด, ๋ท " "๋ ๋ญ๋๊ณ ๋ฌผ์ด๋ด ๋๋ค. ํ ์คํธ๋ง์ " " ๋๊น.
2. \( ) ๊ฐ ๋์ค๋ฉด ๋ณ์๊ฐ ๋ค์ด๊ฐ์ผํฉ๋๋ค. ์ด์ํ ๊ฒ์ ๋ฃ์ผ๋ฉด, ๋ญ๋๊ณ ๋ฐ์ง๋๋ค.
3. \๊ฐ ๋์ค๋ฉด ์ด์ค์ผ์ดํ ๋ฌธ์๋ก ์ฐ์ด๊ฒ ๋ฉ๋๋ค.
๊ทผ๋ฐ ๋ง์ฝ, ์ด๋ฐ ๋ฃฐ์ ํผํ๊ธฐ ๋์ฒํ๋ค๋ฉด, ๊ทธ๋ฅ ๋ณด์ด๋ ๊ทธ๋๋ก์ ์คํธ๋ง์ผ๋ก ๋ง๋ค์ด์คฌ์ผ๋ฉด ์ข๊ฒ ๋ค๋ฉด,
#" ~ "# ์ ์ ํ์ค๋ฅผ ์ฌ์ฉํ์๋ฉด ๋ฉ๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด, ์คํธ๋ง์ ๊ทธ๋ฅ ์ฃผ๋ ๊ทธ๋๋ก, ๋ณด์ด๋ ๊ทธ๋๋ก ๊ฐ์ง๊ณ ์๊ฒ๋ฉ๋๋ค. ํ๋ฆฐํธ๋ ๊ทธ๋๋ ๋์ค์ฃ .
+ ) ์ค์ํํธ ํ์, ์ฌ๋๋ค์ด ์์คํธ๋ง์ ์ฌ์ฉํ๋ฉด์ ๋ง์ฝ, ๊ทธ์์ค์ interpolation์ ํ๊ณ ์ถ์ ๊ฒฝ์ฐ๋ฅผ ์ํด,
์์ธ๋ฅผ ๋จ๊ฒจ๋์์ต๋๋ค.
\#( schoolName ) ์ ๊ทธ๋๋ ์ฌ์ ํ, ์ธํฐํด๋ ์ด์ ์ผ๋ก ํด์๋ฉ๋๋ค.
Substring ์๋ธ ์คํธ๋ง (type)
* ๋ถ๋ชจ ์คํธ๋ง ๋ฐ์ดํฐ๋ก๋ถํฐ, ์ผ์ ๋ฒ์ ๋งํผ ์ฌ๋ผ์ด์ฑํ ๋, ๊ทธ ๊ฒฐ๊ณผ๋ก ์๊ธฐ๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ์ ๋๋ค. ( ๋น์ทํ ๊ธฐ๋ฅ์ด์ง๋ง, ๋ณ๋์ ๋ฐ์ดํฐ ๊ตฌ์กฐ) ์๋์ผ๋ก ๋ณต์ฌ๋์ด, ์๋ก์ด ๋ฐ์ดํฐ๋ก ๋จ์ ์ ์ฅ๋์ง ์์ต๋๋ค.
* ๋จ์์ ์๋ก์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง๋๋ ๊ฒ์ด ์๋๋ผ, ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋ ์ ์์ต๋๋ค. ํ์ง๋ง ์ฌ์ ํ ๋ค์์ ์ ์ฉ๊ฐ๋ฅํ ๋ฉ์๋๋ ์ ์ง๋ฉ๋๋ค.
* ๊ตณ์ด String( )์ ํตํด ๊ฐ์ ์นดํผํด ์๋ก์ด ์คํธ๋ง์ ๋ง๋ค ํ์๋ ์์ต๋๋ค.
* ๋ง์ฝ ์๋ธ ์คํธ๋ง์ ์ด์ฉํด, ํจ์ ๊ฐ์ ๋ฆฌํดํ๊ฑฐ๋, ์คํธ๋ง์ ๋ฐ๋ ํจ์์ ์ธ์๋ก ์ ๋ฌํ ์์๋, String( )์ผ๋ก ํ์ ๋ณํ์ ํด์ฃผ๋ฉด ๋ฉ๋๋ค.
let fullName = "Han seokhee"
let spaceIndex = fullName.firstIndex(of: " ")! // String.Index? ์ต์
๋ ๋ฆฌํด์ด๋ผ ์ด๋ ๊ฒ // Han seokhee
var firstName = fullName[ fullName.startIndex ..< spaceIndex ] // Han
// -> "Han" ( String.subsequence type )
// ๋ถ๋ชจ ์คํธ๋ง์ธ ํ ๋ค์์ ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๊ธฐ์ํ์ฌ ์ด๋๋ ์ฑ๋ง ์ํ์ฌ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์,
// ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์๋๋๋ค.
// < Range >
// ์๋ธ์คํธ๋ง : [ ์ธ๋ฑ์ค ๋ ์ธ์ง ]
// ๋ ์ธ์ง ์ข
๋ฅ
// close-ended : spaceIndex ..< secondSpaceIndex
// open-ended : spaceIndex ... : spaceIndex ~๋ถํฐ ๋๊ฐ์ง
// fullNamep[ ..< spaceIndex ] : ์ฒ์๋ถํฐ ~๊น์ง
// ์คํ์ด์ค ์ธ๋ฑ์ค ๋ค์ ์ธ๋ฑ์ค๋ถํฐ
// * ์ปฌ๋ ์
์ ๋๊น์ง
fullName[ fullName.index(after: spaceIndex)... ] // seokhee
fullName[ ...fullName.index(before: spaceIndex) ] // Han
String ์ผ๋ก ๋ฐ๊พธ์ด์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด? casualํ๊ฒ String( String.subsequence๊ฐ ) ์ ์ด์ฉํ๋ฉด ๋ฉ๋๋ค.
String(firstName) // -> type : String
// copied -> new memory usage
์ฐ์ตํด๋ณด์ธ์ !!
// Substring
let fullName3 = "sucky Han"
let target1Index = fullName3.firstIndex(of: " ")!
let target2Index = fullName3.index(after: target1Index)
let subString1 = fullName3[ ..<target2Index ]
let firstIndex = subString1.firstIndex(of: "s")!