見出し画像

Swiftでプログラミング。 - 文字列と文字 3

Accessing and Modifying a String 文字列へのアクセスと変更

文字列にアクセスして変更するには、そのメソッドとプロパティを使用するか、添え字構文を使用します。

String Indices 文字列インデックス

各文字列には、文字列内の各文字の位置に対応するインデックス、String.Indexが関連付けられています。

上記のように、文字が異なれば保存に必要なメモリ量も異なる可能性があるため、特定の位置にある文字を判別するには、その文字列の最初または最後から各Unicodeスカラーを繰り返し取得する必要があります。このため、Swift文字列に整数値でインデックスを付けることはできません。

startIndexプロパティを使用して、文字列の最初の文字の位置にアクセスします。 endIndexプロパティは、文字列の最後の文字の後の位置です。その結果、endIndexプロパティは文字列の添え字に対する有効な引数ではありません。文字列が空の場合、startIndexとendIndexは等しくなります。

Stringのindex(before :)メソッドとindex(after :)メソッドを使用して、特定のインデックスの前後のインデックスにアクセスします。指定されたインデックスからさらに離れたインデックスにアクセスするには、これらのメソッドの1つを複数回呼び出す代わりに、index(_:offsetBy :)メソッドを使用できます。

添え字構文(subscript)を使用して、文字列インデックスを指定して特定の文字にアクセスできます。

   let greeting = "Guten Tag!"
   greeting[greeting.startIndex]
   // G
   greeting[greeting.index(before: greeting.endIndex)]
   // !
   greeting[greeting.index(after: greeting.startIndex)]
   // u
   let index = greeting.index(greeting.startIndex, offsetBy: 7)
   greeting[index]
   // a

文字列の範囲外のインデックスにアクセスしようとすると、ランタイムエラーが発生します。

   greeting[greeting.endIndex] // Error
   greeting.index(after: greeting.endIndex) // Error

インデックスを使用して、文字列内の個々の文字にアクセスします。

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

コレクションプロトコルに準拠する任意のタイプで、startIndexプロパティとendIndexプロパティ、およびindex(before :)、index(after :)、およびindex(_:offsetBy :)メソッドを使用できます。これには、ここに示すように文字列と、配列、辞書、セットなどのコレクションタイプが含まれます。

Inserting and Removing 挿入と取り外し

指定されたインデックスの文字列に単一の文字を挿入するには、insert(_:at :)メソッドを使用し、指定されたインデックスの別の文字列の内容を挿入するには、insert(contentsOf:at :)メソッドを使用します。

   var welcome = "hello"
   welcome.insert("!", at: welcome.endIndex)
   // welcome now equals "hello!"
   welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
   // welcome now equals "hello there!"

指定されたインデックスの文字列から1文字を削除するには、remove(at :)メソッドを使用します。指定された範囲部分の文字を削除するには、removeSubrange(_ :)メソッドを使用して特定の文字を指定します。

   welcome.remove(at: welcome.index(before: welcome.endIndex))
   // welcome now equals "hello there"
   let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
   welcome.removeSubrange(range)
   // welcome now equals "hello"

Substrings   部分文字列

文字列から部分文字列を取得すると(たとえば、添え字またはprefix(_ :)などのメソッドを使用して)、結果は別の文字列でなく別の部分文字列のインスタンスになります。 Swiftの部分文字列には、前述の文字列で行ったメソッドとほとんど同じように操作できます。つまり、文字列を操作するのと同じ方法で部分文字列を操作できます。 ただし、部分文字列は文字列ではないため短い間使う場合のみ部分文字列を使用します。 長期間使う場合は部分文字列を文字列のインスタンスに変換します。 例えば:

   let greeting = "Hello, world!"
   let index = greeting.firstIndex(of: ",") ?? greeting.endIndex
   let beginning = greeting[..<index]
   // beginning is "Hello"
   // Convert the result to a String for long-term storage.
   let newString = String(beginning)

文字列と同様に、各部分文字列には、部分文字列を構成する文字が格納されるメモリ領域があります。文字列と部分文字列の違いは、パフォーマンスの最適化として、部分文字列は元の文字列を格納するために使用されるメモリの一部、または別の部分文字列を格納するために使用されるメモリの一部を再利用できることです。 (文字列にも同様の最適化がありますが、2つの文字列がメモリを共有する場合、それらは等しくなります。)このパフォーマンスの最適化は、文字列または部分文字列のいずれかを変更するまで、メモリをコピーするパフォーマンスコストを支払う必要がないことを意味します。上記のように、部分文字列は長期保存には適していません。元の文字列のストレージを再利用するため、部分文字列のいずれかが使用されている限り、元の文字列全体をメモリに保持する必要があります。

上記の例では、挨拶は文字列です。つまり、文字列を構成する文字が格納されるメモリ領域があります。 beginはgreetingの部分文字列であるため、greetingが使用するメモリを再利用します。対照的に、newStringは文字列です。部分文字列から作成される場合、独自のストレージがあります。次の図は、これらの関係を示しています。

StringとSubstringはどちらもStringProtocolプロトコルに準拠しています。つまり、文字列操作関数がStringProtocol値を受け入れると便利なことがよくあります。 このような関数は、String値またはSubstring値のいずれかを使用して呼び出すことができます。

Comparing Strings   文字列の比較

Swiftは、テキスト値を比較する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")
   }
   // Prints "These two strings are considered equal"

2つの文字列値(または2つの文字値)は、それらの拡張書記素クラスターが正規に同等である場合、等しいと見なされます。 拡張書記素クラスターは、舞台裏で異なるUnicodeスカラーから構成されている場合でも、同じ言語的意味と外観を持っている場合、正規に同等です。

たとえば、ラテン文字Eとアキュートアクセント(U + 00E9)は、ラテン文字E(U + 0065)の後にアキュートアクセント(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"

逆に、英語で使用されるラテン大文字A(U + 0041、または「A」)は、ロシア語で使用されるキリル大文字A(U + 0410、または「А」)と同等ではありません。 文字は視覚的に似ていますが、同じ言語的意味はありません。

    let latinCapitalLetterA: Character = "\u{41}"
   let cyrillicCapitalLetterA: Character = "\u{0410}"
   if latinCapitalLetterA != cyrillicCapitalLetterA {
       print("These two characters aren't equivalent.")
   }
   // Prints "These two characters aren't equivalent."
Swiftでの文字列と文字の比較は、ロケールに依存しません。

Prefix and Suffix Equality  接頭辞と接尾辞の同等性

文字列に特定の接頭辞または接尾辞があるかどうかを確認するには、文字列のhasPrefix(_ :)メソッドとhasSuffix(_ :)メソッドを呼び出します。どちらも、String型の単一の引数を取り、ブール値を返します。

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

定数romeoAndJuliet(配列)でhasPrefix(_ :)メソッドを使用して、劇の第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"
hasPrefix(_ :)メソッドとhasSuffix(_ :)メソッドは、文字列と文字の等価性で説明されているように、各文字列の拡張書記素クラスター間で文字ごとの正規等価性比較を実行します。

Unicode Representations of Strings  文字列のUnicode表現

Unicode文字列がテキストファイルまたはその他のストレージに書き込まれると、その文字列のUnicodeスカラーは、Unicodeで定義されたいくつかのエンコード形式のいずれかでエンコードされます。各フォームは、コードユニットと呼ばれる小さなチャンクで文字列をエンコードします。これらには、UTF-8エンコード形式(文字列を8ビットコード単位としてエンコードする)、UTF-16エンコード形式(文字列を16ビットコード単位としてエンコードする)、およびUTF-32エンコード形式(エンコードする)が含まれます。 32ビットコード単位としての文字列)。

Swiftは、文字列のUnicode表現にアクセスするためのいくつかの異なる方法を提供します。 for-inステートメントを使用して文字列を反復処理し、Unicode拡張書記素クラスターとして個々の文字値にアクセスできます。このプロセスについては、「文字の操作」で説明しています。

または、他の3つのUnicode準拠表現のいずれかで文字列値にアクセスします。

UTF-8コードユニットのコレクション(文字列のutf8プロパティでアクセス)
UTF-16コードユニットのコレクション(文字列のutf16プロパティでアクセス)
文字列のUTF-32エンコード形式(文字列のunicodeScalarsプロパティでアクセス)に相当する21ビットのUnicodeスカラー値のコレクション

以下の各例は、文字D、o、g、!(DOUBLE EXCLAMATION MARK、またはUnicodeスカラーU + 203C)と🐶文字(DOG FACE、またはUnicodeスカラー)で構成される次の文字列の異なる表現を示しています。 U + 1F436):

let dogString = "Dog‼🐶"

UTF-8 Representation  UTF-8 表現

utf8プロパティを反復処理することにより、文字列のUTF-8表現にアクセスできます。 このプロパティのタイプはString.UTF8Viewです。これは、文字列のUTF-8表現の各バイトに1つずつ、符号なし8ビット(UInt8)値のコレクションです。

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

上記の例では、最初の3つの10進codeUnit値(68、111、103)は、文字D、o、およびgを表し、そのUTF-8表現はASCII表現と同じです。 次の3つの10進codeUnit値(226、128、188)は、DOUBLE EXCLAMATIONMARK文字の3バイトのUTF-8表現です。 最後の4つのcodeUnit値(240、159、144、182)は、DOGFACE文字の4バイトのUTF-8表現です。

UTF-16 Representation UTF-16表現

utf16プロパティを反復処理することにより、文字列のUTF-16表現にアクセスできます。 このプロパティのタイプはString.UTF16Viewです。これは、文字列のUTF-16表現の16ビットコードユニットごとに1つずつ、符号なし16ビット(UInt16)値のコレクションです。

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

この場合も、最初の3つのcodeUnit値(68、111、103)は文字D、o、およびgを表し、そのUTF-16コード単位は文字列のUTF-8表現と同じ値を持ちます(これらのUnicodeスカラーはASCII文字を表すため) )。

4番目のcodeUnit値(8252)は、16進値203Cに相当する10進値であり、DOUBLE EXCLAMATIONMARK文字のUnicodeスカラーU + 203Cを表します。 この文字は、UTF-16では単一のコード単位として表すことができます。

5番目と6番目のcodeUnit値(55357と56374)は、DOGFACE文字のUTF-16サロゲートペア表現です。 これらの値は、U + D83Dの高サロゲート値(10進値55357)とU + DC36の低サロゲート値(10進値56374)です。

Unicode Scalar Representation Unicodeスカラー表現

unicodeScalarsプロパティを反復処理することにより、文字列値のUnicodeスカラー表現にアクセスできます。 このプロパティはUnicodeScalarView型であり、UnicodeScalar型の値のコレクションです。

各UnicodeScalarには、UInt32値内で表されるスカラーの21ビット値を返すvalueプロパティがあります。

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

最初の3つのUnicodeScalar値(68、111、103)の値プロパティは、再び文字D、o、およびgを表します。

4番目のcodeUnit値(8252)も、16進値203Cと10進数で同等です。これは、DOUBLE EXCLAMATIONMARK文字のUnicodeスカラーU + 203Cを表します。

5番目で最後のUnicodeScalarのvalueプロパティ128054は、DOGFACE文字のUnicodeスカラーU + 1F436を表す16進値1F436と10進数で同等です。

値のプロパティをクエリする代わりに、各UnicodeScalar値を使用して、文字列補間などで新しい文字列値を作成することもできます。

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


この記事が気に入ったらサポートをしてみませんか?