Swiftの文字列型(Strings and Characters)

Swiftの文字列型 | Strings and Characters

Swiftの文字列型はStringです。また、1文字単位で取り扱うためのCharacterという型もあります。

ここでは、まずStringCharacterについて簡単に紹介した後に、文字列リテラルとは何か?、空白文字で初期化する方法について説明します。Object-Cとの対比で”string mutability”について少し触れ、Stringは値型であること、StringからCharacterを取り出す方法を、具体例を交えつつ説明します。最後に、文字列の連結と文字列補間について、サンプルコードと一緒に説明します。

文字列型Stringと文字型Character

複数の文字ならString、1文字単位ならCharacter

文字列型(StringSwiftの基本的な型の1つで、その名前の通り、例えば

"hello world"

など、複数の文字のかたまり(文字列)を取り扱う型です。

一方、文字型であるCharacterは、1文字単位で文字を管理する型です。「1文字単位」という言葉が具体的に何を指すのかが肝なのですが、これについては後で詳しく説明します。

簡単にまとめると、

  • String: 複数の文字(文字列)を取り扱う型
  • Character: 1文字単位で文字を取り扱う型

です。

StringはCharacterの集合体?

文字列は文字の集合ですから、StringCharacterの配列のような取扱いが出来そうです。後述しますが、実際にそのように実装されています。

ただし、StringCharacterは全く独立の型ですから、あくまでStringCharacterの配列のように取り扱える機能がある、というだけです。

配列ArrayはSwiftのコレクション型の1つで、同じ型を持った複数の値をひとまとめにして取り扱うための箱のようなものです。

文字列リテラル | String Literals

文字列リテラル = ダブルクォーテーションマーク””で囲んだ文字列

文字列リテラル(String Literals)は、何かしらの値を文字列として取り扱うための書式で、ダブルクォーテーションマーク""で囲まれた文字列全体を指します。

一番最初に例として出した、

"hello world"

も文字列リテラルです。

また、Swiftでは文字(列)リテラルとしてシングルクォーテーション''は使えません。

// シングルクォーテーションは使えない
'hello world'
'a' // 1文字でもだめ
//コンパイルエラー
//Single-quoted string literal found, use "

変数や定数の初期値、変数への値の代入として使う

変数や定数を文字列型として初期化したい場合に、文字列リテラルが使えます。

// Stringとして初期化
var greeting = "hello world" // 変数
let thisYear = "2016" // 定数

文字列リテラルで初期化した変数や定数はStringとして取り扱われます。これは、Swiftの型推論(type inference)機能のおかげです。したがって、文字列を取り扱う場合は、明示的にStringという型を付けなくても大丈夫です。

// 代入
greeting = "こんにちわ"

また、変数の場合は、後で値を変更することが出来ますので、上記の例のように別の文字列リテラルを代入することも可能です。

空白文字で初期化する | Initializing an Empty String

空のダブルクォーテーション、またはString初期化子

空白文字を作る方法は2つあります。

// ダブルクォーテーションマークのみ
var emptyString = ""

// String初期化子で空白文字
var anotherEmptyString = String()

1つ目はダブルクォーテーションマーク""のみ(間に何も挟まない)で変数(または定数)を初期化する方法です。これは簡単だと思います。

2番目の方法は少し難しいですが、上記サンプルコードのように、Stringの型名の後ろに空のカッコ()を付ける方法でも空白文字を作れます。

2番目の方法は初期化子(initializer)という特別な機能を使っています。初期化子に関しては別のページで詳しく説明します。初期化子はC言語で言う所のコンストラクタです。この後少し触れますが、Swiftの基本的な型は全て構造体(structures)ですから、初期化子のような機能がデフォルトで備わっています。

isEmptyプロパティで空白文字かどうかチェック

文字列を入れた変数や定数が空白だと困る場合がありますが、それを確認する方法も用意されています。

if emptyString.isEmpty {
    print("空白文字列です")
}
//"空白文字列です"と表示

String型がもつプロパティisEmptyは、空白文字ならtrue、そうでないならfalseを返します。

上記サンプルコードでemptyString変数は空白文字なので、isEmptytrueを返します。if文の条件判定がtrueになると、その中身が実行されるので、「空白文字列です」という文章が出力されます。

補足1:
プロパティ(properties)というのは、型内部にスコープが限定された変数や定数のようなものです。プロパティについては、別のページで詳しく説明します。
補足2:
if文は条件分岐の1つで、ifに続く条件がtrueならif文の中身を実行、falseなら何もせずにスキップするという機能です

変数と定数 | String Mutability

“String Mutability”というのは難しい言葉ですが、日本語にすると

文字列型が変更出来るかどうか

ということです。「変更できる」という部分が「can be modified or mutated」と公式マニュアルで表現している箇所になります。「変更できるか」というのは、結局変数にするか定数にするか、という話です。

変数と定数の宣言は、文字列も他の型と同様で、

// 変数と定数
var variable = "変更可能な"
variable += "文字列"

let constant = "変更できない文字列"
constant += " 定数なので変更不可能" // <- コンパイルエラー
//note: change 'let' to 'var' to make it mutable

変数にする場合はvarで宣言し、定数にする時はletで宣言します。当然ですが、定数にすると後で変更することは出来ません。

一見自明なことのようですが、公式マニュアルでわざわざ1セクション割いてまで説明したのは、Object-Cでは変更可能な文字列(NSMutableString)と変更不可能な文字列(NSString)の2つが用意されているからだと思います。

“NOTE

This approach is different from string mutation in Objective-C and Cocoa, where you choose between two classes (NSString and NSMutableString) to indicate whether a string can be mutated.”

抜粋:: Apple Inc. “The Swift Programming Language (Swift 3)”。 iBooks https://itun.es/jp/jEUH0.l

Stringは値型 | String Are Value Types

構造体と値型

SwiftのStringは、実は構造体(structures)で作られており、構造体は値型(value types)です。つまり「Stringは値型である」と言えます。

構造体については別ページで詳しく説明しますが、クラス(classes)などのようなカプセル化を実現するためのツールの1つです。

値型の詳細については割愛しますが、基本的には

値型 = 代入等の演算が行われた時に複製(コピー)されるような型

です。値型では無い型は「参照型(reference types)」と呼ばれており、クラスや関数(functions)がこれに該当します。

値型と参照型の違いが簡単に分かる具体例

値型と参照型の違いが簡単に分かる具体例をサンプルコードで作ってみました。

// 構造体は値型
struct StringStruct { var string = "struct" }
var instance1 = StringStruct()
var instance2 = instance1
instance1.string = "this is instance1"
print(instance1.string, instance2.string, separator:", ")
//"this is instance1, struct"と表示

// クラスは参照型
class StringClass { var string = "class" }
var instance3 = StringClass()
var instance4 = instance3
instance3.string = "this is instance3"
print(instance3.string, instance4.string, separator:", ")
//"this is instance3, this is instance3"と表示

対比しやすいように敢えて独自の型を作りましたが、struct StringStructの部分は、Swiftの基本型(StringIntなどなど)で置き換えても同じです。

結果を見ると一目瞭然ですが、値型の場合

print(instance1.string, instance2.string, separator:", ")
//"this is instance1, struct"と表示

オリジナルのinstance1instance2に代入し、その後instance1stringを編集すると、instance1instance2の持つstringの文字列は違っています。一見当たり前のような気もしますが、これが値型の特徴で、値の割り当てに対して(基本的に)値が複製されます。

一方で、参照型であるクラスの場合、

print(instance3.string, instance4.string, separator:", ")
//"this is instance3, this is instance3"と表示

一方の変更(instance3)が別の変数(instance4)にも影響を与えています。参照型の場合、instance3instance4の指す実体(インスタンス)は1つだけなので、1つの変更が全体に影響を与えることになります。詳しくはクラスと構造体のページで説明します。

公式マニュアルに書いてありますが、値型でも無闇にコピーされるという訳ではないようです。

“Behind the scenes, Swift’s compiler optimizes string usage so that actual copying takes place only when absolutely necessary. This means you always get great performance when working with strings as value types.”

抜粋:: Apple Inc. “The Swift Programming Language (Swift 3)”。 iBooks https://itun.es/jp/jEUH0.l

StringからCharacterを取り出す方法 | Working with Characters

文字列は文字の集合ですから、StringCharacterの配列のような取扱いが出来そうです。

と書きましたが、実際に試してみます。

StringからCharacterの配列にアクセス

let dog = "犬!イヌ!🐶"
for character in dog.characters {
    print(character)
}
//犬
//!
//イ
//ヌ
//!
//🐶

公式マニュアルとほとんど同じですが、文字列として定義した定数dogから、charactersというプロパティを使ってCharacterの配列にアクセス出来ることが分かります。後はfor-inループで配列要素を一つずつ取り出して表示しているだけです。

また、公式マニュアルにあるように、文字列リテラルから直接charactersプロパティにアクセスすることも可能です。

// 文字列リテラルから呼び出すことも可能
for character in "犬!イヌ!🐶".characters {
    print(character)
}

Characterの配列からStringを作る

上記のサンプルコードでは、元々Stringだった文字列からCharacterの配列を取り出しました。逆変換も当然可能で、Characterの配列を作ってそれをStringにすることも出来ます。

公式マニュアルの例をほぼ流用すると、

let catCharacters: [Character] = ["猫", "!", "ネ", "コ", "!", "😺"]
let cat = String(catCharacters)
print(cat)
//"猫!ネコ!😺"と表示

このようになります。

余り機会がないかもしれませんが、Character変数(または定数)を作るときには、明示的に型を指定する必要があります。

let object: Character = "物" // Character
let objectString = "物" // String

Swiftでは1文字だけの場合、デフォルトでStringに型推論されます。

文字(列)の連結 | Concatenating Strings and Characters

連結して新しい文字列を作る | +演算子

StringCharacterで定義された値や、文字列リテラルは+演算子を使って連結することが出来ます。連結された文字列はStringになります。

let hello = "hello"
let world = "world!"
let helloWorld = hello + " " + world
print(helloWorld)
//"hello world!"と表示

要するに文字(列)の加算ですが、これを特別に"cocatenate"(名詞だとcocatenation)と呼んでいます。和訳を当てると「つなぐ」とか「連結する」という意味です。

既存の文字列に新しい文字列を追加する | +=演算子

+演算子だけでなく、既存の文字列に別の文字(列)を追加する複合代入演算子+=も使えます。

var animal = "😺"
animal += "🐶"
print(animal)
//"😺🐶"と表示

既存の文字列に変更を加えることになるので、この場合変数で定義しておかないとコンパイルエラーになります。

StringにCharacterを連結する方法 | appendメソッド

また、Stringが持つメソッドappend(_:)を使うと、Characterで定義された文字を追加することも可能です。

let exclamationMark: Character = "!"
//animal += exclamationMark // これはダメ
animal.append(exclamationMark)
print(animal)
//"😺🐶!"

StringCharacterは型が違うので、直接複合代入演算子で追加することは出来ません(型変換すれば可能)。

公式マニュアルに注意書きがありますが、既存のCharacterに新たな文字(列)を追加することは出来ません(Characterはそもそも1文字だけの型なので)。

“NOTE

You can’t append a String or Character to an existing Character variable, because a Character value must contain a single character only.”

抜粋:: Apple Inc. “The Swift Programming Language (Swift 3)”。 iBooks https://itun.es/jp/jEUH0.l

文字列補間 | String Interpolation

文字列補間(String Interpolation)とは、文字列の中に埋め込まれた変数・定数またはリテラル等を評価して、その値を文字列として表示することです。

文字列の中に変数や定数を差し込むには、カッコ()の直前にバックスラッシュ\を置き、その中に変数等を入れます。

let year = 2016
let string1 = "今年は\(year)年です"
//"今年は2016年です"と表示

上記サンプルコードだと"\(year)"の部分で、既存の整数yearを文字列の中に放り込んでいます。文字列補間を使うと、既存の変数等を展開してその中身を出力することが出来るので大変便利です。

また、既存の変数などだけでなく、文字列補間中に演算することも出来ます。

// 単純な四則演算
let two = 2
print("1 + 2 + 3 = \(1 + two + 3)")

// 関数
func add(a: Double, b: Double) -> Double { return a + b }
print("4.2 + 5.4 = \(add(a:4.2, b:5.4))")

値やリテラル等の四則演算だけでなく、関数を使った複雑な演算も可能です。上記サンプルコードでは戻り値を持った関数を使いましたが、戻り値が無い場合でも問題なく動きます。

まとめ

  • Stringは文字列を取り扱う型
  • 1文字だけの場合はCharacterを使っても良い
  • 文字列リテラルは""で囲まれた文字列
  • 空白文字で初期化するには(1)空の文字列リテラル""、または(2)String初期化子を使う
  • isEmptyプロパティで空白文字かどうかチェック可能
  • 変更可能な文字列は変数(var)、変更出来ない文字列は定数(let)で定義
  • Stringは(構造体なので)値型
  • StringからCharacter配列にアクセス可能。その逆も出来る
  • 文字列の連結(concatenate)には++=)演算子
  • 文字列補間(string interpolation)で変数や数値リテラル等を文字列に組み込める
Swiftの文字列型 | Unicodeと文字列のUnicode表現