Swift Programming Language Stringまとめ


## 文字列と文字
+ `String` type 
+ `Character` type

### 空文字列の初期化
```
var emptyString = ""               // 空文字列リテラル
var anotherEmptyString = String()  // 初期化シンタックス
// 2 つの文字列は共に空で、等価
```
Boolの`isEmpty`propertyで、String valueが空かどうかを確認できます。

### String typeは値型
関数やメソッドに渡される時、定数や変数に代入される時、String valueはコピーされる
どのケースにおいても、original versionではなく、copyされたString valueが生成される
=> default copyなStringの動作により、String valueが関数やmethodに渡された時に、自分自身で変更しない限り、渡された文字列が変更されていないことに確信できる

### 文字の扱い
`for-in` roopでStringのCharacter valueにaccessできる

```
for character in "Dog!🐶".characters {
    print(character)
}
// D
// o
// g
// !
// 🐶
```

あるいは、Character typeのアノテーションで、1 文字の文字列リテラルから独立した Character 定数や変数を生成することができます。

```
let exclamationMark: Character = "!"
```

こんなこともできちゃう

```
let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)
// "Cat!🐱" と出力
```

### 文字列及び文字の結合
```
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcome は "hello there"

var instruction = "look over"
instruction += string2
// instruction は "look over there"

let exclamationMark: Character = "!"
welcome.append(exclamationMark)
// welcome は "hello there!"
```
`Character`変数に`String`や`Character`を付け足すことはできない

### String Interpolation(文字列補間法)　　補間：内側に挿入すること
文字列リテラル内に値を含め、定数や変数、リテラル、式を組み合わせて新しい String 値を構築する手段

```
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message は "3 times 2.5 is 7.5"
```
**注意：補間文字列の丸括弧内には、escapeしていない`\`やキャリッジリターン、ラインフィードを含めることはできません。ですが、他の文字列リテラルを含めることは可能**

### Unicode
+ Unicode は、異なる表記体系間でのテキストの符号化、表現、処理に関する国際規格
+ 標準化された形式で多言語の文字を表現し、text fileや Web pageなどの外部sourceと文字を読み書きすることができる

#### Unicode scalars
+ Swift の String 型は、Unicode スカラ値で構成されている
+ Unicode scalarsは、`LATIN SMALL LETTER A ("a")`が`U+0061`で、`FRONT-FACING BABY CHICK ("🐥")`が `U+1F425`というように文字や修飾文字を表現する固有な 21 ビット数値

#### Special Characters in String Literals
+ escapeされた特殊文字 `\0`（null 文字), `\\`, ` \t`（水平タブ）, `\n`（ラインフィード） , `\r`（キャリッジリターン）, `\"`, `\'`
+ 任意の Unicode scalarsで、`\u{n}`の`n`が1-8桁で、有効なUnicode コードポイントに一致する16進数値

```
let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"        // $,  Unicode スカラ U+0024
```

#### Extended Grapheme Clusters(拡張書記素クラスタ)
+ Swift の`Character`typeのすべてのinstanceは、拡張書記素クラスタを表現している
+ 拡張書記素クラスタは、人間が読める文字にするために 1 以上の Unicode scalarsを組み合わせたarray

例として、文字 é は、1 つの Unicode スカラ é (LATIN SMALL LETTER E WITH ACUTE または U+00E9) として表現できます。一方で、同じ文字をスカラ 1 組で表現することも可能です。標準文字 e (LATIN SMALL LETTER E または U+0065) に続けて、スカラ COMBINING ACUTE ACCENT (U+0301) とします。スカラ COMBINING ACUTE ACCENT は、Unicode を認識するテキストレンダリングシステムによって適用され、e が é に描き変えられます。

両方のケースで、文字 é は、拡張書記素クラスタを表現する Swift の Character 値として表現されています。初めのケースでは、クラスタは 1 つのスカラを含んでいます。次のケースでは、2 つのスカラを含んでいます。

```
let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e に続けて  ́
// eAcute は é で、combinedEAcute も é

let regionalIndicatorForUS: Character = "\u{1F1FA}\u{1F1F8}"
// regionalIndicatorForUS は 🇺🇸
```

### Counting Characters
`.count`でString valueのCharacter valueのcountを取得できる

#### memo
拡張書記素クラスタは、1 以上の Unicode scalarsで構成されます。これは、文字によって（また、同じ文字でも異なる表現によって）保存に必要なmemory量が異なるということです。このため、文字列の表現内で各文字が同じmemory量となるわけではありません。結果として、拡張書記素クラスタの境界を特定するために文字列内を繰り返し処理することなく、文字列の文字数を算出することはできません。特に長い文字列の値を扱う場合、characters propertyがその文字列の文字を特定するために、文字列全体の Unicode  scalarsを繰り返し処理する必要があることを知っておいてください。

characters propertyによって返される文字数は、同じ文字を含む NSString の length propertyと常に同じにはなりません。NSString の長さは、文字列の UTF-16 表現内の 16bit 符号単位の数をベースにしており、文字列内の Unicode 拡張書記素クラスタの数ではありません。

### Accessing and Modifying a String
#### String Indices(文字列index)
+ 各String valueには、文字列内での各`Character`の位置に対応するindex型`String.Index`を持ちます。
+ 文字によって異なるメモリ量が必要で、どの`Character`がどの位置にあるかを特定するために、`String` の最初から最後までの各Unicode scalarsを繰り返し処理する必要があります。そのため、Swift の文字列は整数値でindexすることができません。

`startIndex`: 最初の`Character`にアクセス
`endIndex`: 最後の`Character`の後にアクセス。Stringのサブスクリプト引数として有効ではない
`predecessor()`: 直前の`Character`にアクセス
`successor()`: 直後の`Character`にアクセス
`advanceBy(_:)`: Int valueを引数にn番目の`Character`にアクセス
範囲外にaccessすると実行時errorが発生する

```
for index in greeting.characters.indices {
    print("\(greeting[index]) ", terminator: "")
}
// "G u t e n   T a g ! " と出力
```

#### Inserting and Removing
```
var welcome = "hello"
welcome.insert("!", atIndex: welcome.endIndex)
// welcome は "hello!"

welcome.insertContentsOf(" there".characters, at: welcome.endIndex.predecessor())
// welcome は "hello there!"

welcome.removeAtIndex(welcome.endIndex.predecessor())
// welcome は "hello there"

let range = welcome.endIndex.advancedBy(-6)..<welcome.endIndex
welcome.removeRange(range)
// welcome は "hello"
```

### Comparing Strings
3通りの比較の仕方がある
+ 文字列および文字の一致
+ 前方一致
+ 後方一致 
 
#### String and Character Equality
`==`か`!=`で比較する

```
let quotation = "We're a lot alike, you and I."
let sameQuotation = "We're a lot alike, you and I."
if quotation == sameQuotation {
    print("These two strings are considered equal")
}
// "These two strings are considered equal" と出力
```

#### Prefix and Suffix Equality
+ String typeの引数を一つ取り、`hasPrefix(_:)`と`hasSuffix(_:)`で調べる
+ Bool valueを返す

```
let romeoAndJuliet = [
    "Act 1 Scene 1: Verona, A public place",
    "Act 1 Scene 2: Capulet's mansion",
    "Act 1 Scene 3: A room in Capulet's mansion",
    "Act 1 Scene 4: A street outside Capulet's mansion",
    "Act 1 Scene 5: The Great Hall in Capulet's mansion",
    "Act 2 Scene 1: Outside Capulet's mansion",
    "Act 2 Scene 2: Capulet's orchard",
    "Act 2 Scene 3: Outside Friar Lawrence's cell",
    "Act 2 Scene 4: A street in Verona",
    "Act 2 Scene 5: Capulet's mansion",
    "Act 2 Scene 6: Friar Lawrence's cell"
]

var act1SceneCount = 0
for scene in romeoAndJuliet {
    if scene.hasPrefix("Act 1 ") {
        act1SceneCount += 1
    }
}
print("There are \(act1SceneCount) scenes in Act 1")
// "There are 5 scenes in Act 1" と出力

var mansionCount = 0
var cellCount = 0
for scene in romeoAndJuliet {
    if scene.hasSuffix("Capulet's mansion") {
        mansionCount += 1
    } else if scene.hasSuffix("Friar Lawrence's cell") {
        cellCount += 1
    }
}
print("\(mansionCount) mansion scenes; \(cellCount) cell scenes")
// "6 mansion scenes; 2 cell scenes" と出力
```

### Unicode Representations of Strings(文字列のUnicode表現)
+ 3通り表現の仕方があり、それぞれで出力が異なる
+ 詳細は本文に。