LoginSignup
3
3

More than 5 years have passed since last update.

String in Swift(日本語)

Last updated at Posted at 2016-05-02

はじめに

公式ドキュメント

公式ドキュメントのうち、ほぼ同じ内容の部分を排除して、ざっくりと日本語訳した。具体例とかは削りぎみ(最後の方直訳っぽくなってしなったので修正したい)
内容の抜け落ちは殆ど無いはず
ざっくり全部日本語で読みたい人用

Swift 2.2の資料を元に書いています

間違い、修正すべき点など遠慮無くコメントでお願いします

String and Characters

CharacterのCollectionとしてなど、様々な方法でアクセスできる

String Literal

""でくくったのがStringのリテラル

var emptyString = ""               // empty string literal
var anotherEmptyString = String()  // initializer syntax

これらは同じ

emptyString.isEmpty()

で空文字かどうか判断できる

String Mutability

varなら変更可能、letなら変更不可
letなのに変更しようとすると、コンパイル時にエラーがでる

Strings Are Value Types

StringはValue Type = 値渡し
自分が変更しないなら、変更されてないことが保証される

Working with Characters

.charactersプロパティを呼び出すと、一文字ずつCharacter型のオブジェクトとして取り出せる

for character in "Dog!🐶".characters {
    print(character)
}

D
o
g
!
🐶

for-inループについてはこちら

もしくは独立に、一文字のStringリテラルからも、Character型の変数/定数を作れる

let exclamationMark: Character = "!"

CharacterのArrayをStringのイニシャライザに渡すと、合体したStringを作れる

let catCharacters: [Character] = ["C", "a", "t", "!", "🐱"]
let catString = String(catCharacters)
print(catString)

Cat!🐱

Concatenating Strings and Characters

文字列の結合。Stringはoperator (+) で新しいStringが作られる(そしてこれを代入すれば更新できる)

let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
// welcomeは"hello there"と等しくなる

operator (+=) でも同様に動く

CharacterをStringに追加するには、String型のappend()メソッドを使う

let exclamationMark: Character = "!"
var welcome = "hello there".append(exclamationMark)
// welcome now equals "hello there!"

ただし、Characterは一文字しか保持できないので、Characterに対して文字やCharacterを追加することは出来ない

Momo:
StringのArrayは.joinWithSeparator()メソッドで結合できる

String Interpolation

String interpolationとは、定数・変数・リテラル・式の値をStringリテラルの中にいれて、新しいStringをつくること

let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
// message is "3 times 2.5 is 7.5"

"\(multiplier)"というプレースホルダが、実際の値に置き換えられる

ちなみに、()内には、エスケープされていないバックスラッシュ、改行コードは入れられないが、その他はいられる

Unicode

Unicodeは国際的な、エンコーディングの標準
Swiftにおける、StringやCharacterは、完全にUnicodeに準拠している。(本節参照)

Unicode Scalars

SwiftでのString型は、裏では、UnicodeScalar値から作られている
UnicodeScalar値は、文字や記号に対応する、

  • U+0061 は LATIN SMALL LETTER A ("a")
  • U+1F425 は FRONT-FACING BABY CHICK ("🐥")

といった21ビットのユニークな数字

ただし、Unicodeスカラー値は全て文字に割り当てるわけではなく、未来のための予約語もある。また、割り当てられてる文字には、上で示した、 LATIN SMALL LETTER AやFRONT-FACING BABY CHICKといった名前もあることも多い

Special Characters in String Literals

Stringリテラルの中にも特殊文字を入れられる(本節参照)

  • エスケープされた特殊文字
エスケープ 表示
\0 null character
\\ backslash
\t horizontal tab
\n line feed
\r carriage return
\" double quote
\' single quote
  • "\u{n}"

"\u{n}"として、nは、1–8桁の16進数にすると、任意のユニコード文字を入れられる(Unicodeのコードポイントが存在するもののみ)

以下、特殊文字の例
wiseWords定数は2つのダブルコーテーションを含む。
その他のdollarSign, blackHeart, sparklingHeartはユニコードスカラー値フォーマットの例

let wiseWords = "\"Imagination is more important than knowledge\" - Einstein"
// "Imagination is more important than knowledge" - Einstein
let dollarSign = "\u{24}"        // $,  Unicode scalar U+0024
let blackHeart = "\u{2665}"      // ♥,  Unicode scalar U+2665
let sparklingHeart = "\u{1F496}" // 💖, Unicode scalar U+1F496

Extended Grapheme Clusters

SwiftのCharacter型のインスタンスは、全て1つのextended grapheme clusterを示す。1つのextended grapheme clusterは、1つの人間が読める文字を表す
(Unicodeスカラー値は1つまたは複数合わさってextended grapheme clusterを構成する)

※ grapheme cluster = 書記素クラスタ

例:
"é"は、
ユニコードスカラー値é (LATIN SMALL LETTER E WITH ACUTE, or U+00E9)
でも表せるが
e (LATIN SMALL LETTER E, or U+0065) + COMBINING ACUTE ACCENT scalar (U+0301)
という組み合わせでも表現できる

COMBINING ACUTE ACCENT scalarは前の文字に適用されて、Unicode対応のテキストレンダリングシステムによって描画される時に、e + COMBINING ACUTE ACCENTはéに変わる

let eAcute: Character = "\u{E9}"                         // é
let combinedEAcute: Character = "\u{65}\u{301}"          // e followed by ́
// eAcute is é, combinedEAcute is é

Extended grapheme clustersによって、とても柔軟に、多くの複雑な文字を、一つのCharacter型の値として表せる
ハングルの音節は、precomposedとdecomposed sequenceのどちらでも表せるが、Swiftではどちらも、1つのCharacter型の値としてみなされる。

let precomposed: Character = "\u{D55C}"                  // 한
let decomposed: Character = "\u{1112}\u{1161}\u{11AB}"   // ᄒ, ᅡ, ᆫ
// precomposed is 한, decomposed is 한

Extended grapheme clustersのおかげで、囲いマークで他のUnicode文字を囲って、一つのCharacter型の値として扱える (例: COMBINING ENCLOSING CIRCLE, U+20DD)

let enclosedEAcute: Character = "\u{E9}\u{20DD}"
// enclosedEAcute is é⃝

regional indicator symbolsに対応するUnicodeスカラは、単一のCharacter型の値を作るために、pairsに結合される

例:
REGIONAL INDICATOR SYMBOL LETTER U (U+1F1FA)と、REGIONAL INDICATOR SYMBOL LETTER S (U+1F1F8)との組み合わせ

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

Counting Characters

Stringの中のCharacterの数を調べたいなら、String.charactersのcountプロパティを使う

let unusualMenagerie = "Koala 🐨, Snail 🐌, Penguin 🐧, Dromedary 🐪"
print("unusualMenagerie has \(unusualMenagerie.characters.count) characters")
// Prints "unusualMenagerie has 40 characters"

ただし、SwiftのCharacterはextended grapheme clustersを使っているので、Stringの結合/修正しても、character.countに影響を与えないこともある

例:
4文字の単語"word"にCOMBINING ACUTE ACCENT (U+0301)を追加しても、結果のcharacters.countは4のまま。4文字目は、eでなくéになっている

var word = "cafe"
print("the number of characters in \(word) is \(word.characters.count)")
// Prints "the number of characters in cafe is 4"

word += "\u{301}"    // COMBINING ACUTE ACCENT, U+0301

print("the number of characters in \(word) is \(word.characters.count)")
// Prints "the number of characters in café is 4"

Extended grapheme clustersは1つまたは複数のUnicodeScalarから構成されるので、違う文字、さらには同じ文字の違う表現は、使うメモリの量が異なる可能性がある。

たとえ同じ文字列の中であっても、各charactersがおなじ量のメモリを使うわけではなので、文字数はString全てをiterateしないと、extended grapheme clusterの境界がわからず、文字数を数えられない

特に、長いString値を扱う時には、.charactersプロパティは、String中の全てのUnicodeスカラをイテレートしなきゃ決定できないことを知っておいてください

NSString型の.lengthは、UTF-16に基づいているので、.characters.countの結果は、(NSString).lengthの結果と違うこともある。

Accessing and Modifying a String

メソッド、プロパティ、サブスクリプト等でStringを取得・修正できる

String Indices

各String型の値には、関連づいたString.Index型があって、各Characterの位置に対応している

上で述べたように、各Characterの必要とするメモリ容量がわからないので、各UnicodeScalarを先頭もしくは末尾からiterateしないと、どのCharacterがその位置にあるのか特定できない
そのため、SwiftのStringでは、Integerでインデックス出来ない

.startIndexプロパティはStringの先頭のCharacterの位置を、.endIndexプロパティはStringの末尾のCharacterの次の位置を表す。よって、.endIndexはsubscriptとして有効ではない。(例2参照)
Stringが空の時、.startIndex.endIndexと等しい

String.Indexの値は、predecessor()メソッドを呼ぶと直前の文字に、successor()メソッドを呼ぶと直後の文字にアクセスできる
Stringの文字は全て、他のIndexから、このメソッドを繋げるか、もしくはadvancedBy(_:)メソッドを使ってアクセスできる。
もしStringの範囲外のindexを指定すると、runtime errorが発生する

特定のindexにあるCharacterは、subscriptでアクセスできる

let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.endIndex.predecessor()]
// !
greeting[greeting.startIndex.successor()]
// u
let index = greeting.startIndex.advancedBy(7)
greeting[index]
// a

範囲外にアクセスするとエラー

greeting[greeting.endIndex] // error
greeting.endIndex.successor() // error

.characters.indicesプロパティは、各CharacterにアクセスするためのindexesのRangeを生成するので、各Characterを読み出せる

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

Inserting and Removing

Stringの特定の位置に文字を挿入するには、insert(_:atIndex:)メソッドを使う

var welcome = "hello"
welcome.insert("!", atIndex: welcome.endIndex)
// welcome now equals "hello!"

特定の位置に別の文字列を挿入するには、insertContentsOf(_:at:)メソッドを使う

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

特定の位置のCharacterを削除するには、removeAtIndex(_:)メソッドを使う

welcome.removeAtIndex(welcome.endIndex.predecessor())
// welcome now equals "hello there"

特定の範囲のサブ文字列を削除するには、removeRange(_:)メソッドを使う

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

Comparing Strings

Swiftには、テキストの値を比較する方法が3つある
- 文字・文字列の等しさ
- prefixの等しさ
- suffixの等しさ

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")
}
// Prints "These two strings are considered equal"

2つの文字/文字列は、extended grapheme clustersが正準等価(canonically equivalent)なときに等しいとみなされる。extended grapheme clustersは、言語的意味、見た目が同じなら、裏では違うUnicode scalarsからできていても、正準等価である

例:
LATIN SMALL LETTER E WITH ACUTE (U+00E9)は
LATIN SMALL LETTER E (U+0065) + COMBINING ACUTE ACCENT (U+0301)
と等価
これらはともにéを表す。

// "Voulez-vous un café?" using LATIN SMALL LETTER E WITH ACUTE
let eAcuteQuestion = "Voulez-vous un caf\u{E9}?"

// "Voulez-vous un café?" using LATIN SMALL LETTER E and COMBINING ACUTE ACCENT
let combinedEAcuteQuestion = "Voulez-vous un caf\u{65}\u{301}?"

if eAcuteQuestion == combinedEAcuteQuestion {
    print("These two strings are considered equal")
}
// Prints "These two strings are considered equal"

逆に、アメリカで使われるような、
LATIN CAPITAL LETTER A (U+0041, or "A")
と、ロシアで使われるような
CYRILLIC CAPITAL LETTER A (U+0410, or "А")
は等価ではない。
これらは見た目は似ているが、言語的な意味は異なる。

let latinCapitalLetterA: Character = "\u{41}"

let cyrillicCapitalLetterA: Character = "\u{0410}"

if latinCapitalLetterA != cyrillicCapitalLetterA {
    print("These two characters are not equivalent")
}
// Prints "These two characters are not equivalent"

Note:
Swiftにおける文字・文字列の比較は、localeによって出来たり出来なかったりするようなものではないです

Prefix and Suffix Equality

文字列が特定の接頭辞・接尾辞を持っているかは、hasPrefix(_:)メソッド、hasSuffix(_:)メソッドを用いる。共にStringを受け取り、Boolを返す

下記の例は、ロミオとジュリエット/シェイクスピアのはじめの2幕の各シーンの舞台を表すStringのArray

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"
]

hasPrefix(_:)メソッドを使えば、Act 1のシーンの数を数えられる

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

同様に、hasSuffix(_:)メソッドを使えば、Capulet’s mansionの中や、Friar Lawrence’s cellの近くのシーンの数を数えられる

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")
// Prints "6 mansion scenes; 2 cell scenes"

Note:

hasPrefix(_:)hasSuffix(_:)メソッドは、各文字列のextended grapheme clusters同士で、文字単位の正準等価(canonical equivalence)の比較を行う(String and Character Equality参照)

Unicode Representations of Strings

Unicode文字列がテキストファイル等に書きだされたら、文字列中のUnicodeScalarsは何らかのUnicodeで使えるエンコーディング形式にエンコードされる。
各形式は、code unitとして知られる小さなchunkに文字列をエンコードする。
UTF-8形式(8-bit code unitsとして文字列をエンコードする)や、UTF-16形式(16-bit code unitsとして文字列をエンコードする)や、UTF-32形式(32-bit code unitsとして文字列をエンコードする)なども含む。

SwiftにはいくつかUnicode表現にアクセスする方法がある
for-in構文でstring全てiterateすれば、各Unicode extended grapheme clusterにアクセスできる(Working with Characters参照)

他にも、3つのUnicode準拠の表現のいずれかでもStringの値にアクセスできる

  • UTF-8のcode unitの集まり
  • UTF-16のcode unitの集まり
  • 21-bitのUnicodeScalar値(UTF-32形式と等価)

それぞれ、.utf8プロパティ、utf16プロパティ、.unicodeScalarsプロパティでアクセスできる

以下に示す各例は、"Dog‼🐶"の異なる表現である。これは、
D,
o,
g,
‼ (DOUBLE EXCLAMATION MARK, or Unicode scalar U+203C),
🐶 character (DOG FACE, or Unicode scalar U+1F436)
からできている

let dogString = "Dog‼🐶"

UTF-8 Representation

.utf8プロパティをiterateすれば、UTF-8表現を得られる。これはString.UTF8View型で、UTF8表現の各バイト対応するUInt8型(unsigned 8-bit)の値の集まり

Character-utf8-map

for codeUnit in dogString.utf8 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 226 128 188 240 159 144 182

上の例では、最初の3つの数字(68, 111, 103)がD, o, gを表す。これはASCII表現と同じ。
その次の3つのcodeUnitの値(226, 128, 188)は、3バイトの、DOUBLE EXCLAMATION MARKのUTF-8表現で、最後の4つのcodeUnitの値(240, 159, 144, 182)は4バイトの、DOG FACEのUTF-8表現。

UTF-16 Representation

.utf16プロパティをiterateすれば、UTF-16表現を得られる。これはString.UTF16View型で、UTF16表現の各16-bitのcode unitに対応するUInt16型(unsigned 16-bit)の値の集まり

Character-utf16-map

for codeUnit in dogString.utf16 {
    print("\(codeUnit) ", terminator: "")
}
print("")
// 68 111 103 8252 55357 56374

繰り返すが、はじめ3つのcodeUnitの値(68, 111, 103)はD、o、gを表し、これはUTF-8の値と同じ。(何故ならUnicodeScalarはASCII文字を表現しているから)

4番目のcodeUnitの値(8252)は10進数で、16進数の203Cと等価。つまりUnicodeScalarの"U+203C"であるDOUBLE EXCLAMATION MARKを表す。これはUTF-16では単一のcode unitで表現される

5, 6番目のcodeUnitの値(55357, 56374)はDOG FACEのサロゲートペア(1つの文字を表す連続した2つのcode pointのペア)。
ここでは、high-surrogate valueはU+D83D(10進数で55357)、low-surrogate valueはU+DC36(10進数で56374)

Unicode Scalar Representation

.unicodeScalarsプロパティをiterateすれば、Unicode scalar表現を得られる。これはUnicodeScalarView型で、UnicodeScalar型の値の集まり

各UnicodeScalarは.valueプロパティを持ち、scalarの21-bitの値を返す。これはUInt32型として渡される

Character-unicodeScalar-map

for scalar in dogString.unicodeScalars {
    print("\(scalar.value) ", terminator: "")
}
print("")
// 68 111 103 8252 128054

はじめ3つのUnicodeScalarの値(68, 111, 103)のvalueプロパティは、ここでもD、o、gを表す

4番目のcodeUnitの値(8252)は、ここでも、10進数で16進数の203Cと等価で、UnicodeScalarの"U+203C"であるDOUBLE EXCLAMATION MARKを表す。

5, 6番目のUnicodeScalarの.valueプロパティ(128054)は、10進数で、16進数の1F436と等価。これはUnicode scalarのU+1F436、つまりDOG FACEを表す。

他にも、.valueプロパティを得る方法としてだが、各UnicodeScalar値は、string interpolationみたいに新しいStringを作るのにも使える

for scalar in dogString.unicodeScalars {
    print("\(scalar) ")
}
// D
// o
// g
// ‼
// 🐶
3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3