この文字の位置はどこ?文字列のインデックスを取得する方法

Scala 3.3.1
最終更新:2023年12月7日

[AD] scalapediaでは記事作成ボランティアを募集しています

この記事では、特定の文字・文字列が文字列中のどこにあるのかを特定する方法を解説します。

文字や文字列を「indexOf」メソッドで検索する

特定の文字や文字列が文字列中のどの位置にあるのかを調べるには、indexOfメソッドを使用します。

indexOfメソッドを使用すると、対象となる文字や文字列の位置を示すインデックスを取得することができます。

インデックスは0から始まる正の整数です。
人が普通に数えるときの番号から1を引いたものです。

  • 1番目ならインデックスは0
  • 2番目ならインデックスは1
  • ...
  • n番目ならインデックスはn-1

となります。

indexOfメソッドは以下のように定義されています。

引数には文字列、あるいはUnicodeのコードポイントを表す数字を渡します。

Java
public int indexOf(int ch) public int indexOf(String str)
Scala
// Scala風に読み替えるとこうなります def indexOf(ch: Int): Int def indexOf(str: String): Int

文字列中の1文字を検索する

まずは特定の1文字を指定して調べてみましょう。

indexOfメソッドの引数にChar型の値を渡します。

indexOfメソッドが引数に取るのはInt(Javaのint)かStringですが、実はChar型の値を渡すと、Int型へと暗黙的に変換されます。
これはJavaもScalaも共通です。
この挙動のおかげでIntを取る引数にChar型を渡すことができる、というわけです。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexOf('a'))

出力は以下のようになります。
3番目にあるのでインデックスは2となります。

2

文字列の中の文字列を検索する場合も「indexOf

今度は文字列を指定して検索してみましょう。
ここでは、試しに2文字以上の文字列を指定してみます。

使い方は、引数にさきほどのCharの代わりに文字列(String)を与えてあげればOKです。

戻り値は文字列の先頭の文字のインデックスです。
"ap"と指定したら"a"の部分のインデックスが返ってきます。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexOf("ap"))

出力は以下のようになります。
"ap"の"a"は16番目に位置しているのでインデックスは15となります。

15

lastIndexOf」で後ろから順に文字を検索する

indexOfメソッドは前から検索していました。
これに対して、文字や文字列を後ろから検索するにはlastIndexOfメソッドを使います。

lastIndexOfメソッドは以下のように定義されています。

Java
public int lastIndexOf(int ch) public int lastIndexOf(String str)
Scala
// Scala風に読み替えるとこうなります def lastIndexOf(ch: Int): Int def lastIndexOf(str: String): Int

先程と同様、特定の1文字を指定して調べてみましょう。

lastIndexOfメソッドの引数にChar型の値を渡します。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.lastIndexOf('c'))

出力は以下のようになります。
2つある"c"のうち、後ろの"c"は13番目にあるので、インデックスは12となります。

12

indexOfSliceで一連の文字を検索する

ScalaのStringにはindexOfSliceメソッドが生えています。
このメソッドを使うと、文字列に含まれる連続する文字を検索することができます。

実はこのindexOfSliceメソッドは、StringではなくSeqOpsが持っているメソッドです。
SeqにおけるindexOf(str: String)に相当するメソッドとして実装されています。

Stringの場合にはindexOfメソッドで同じことができるので、実際のところ使う必要性があまりないのですが、 「Seq[Char]ならある」という場合には便利に使うことができます。

例えばStringではないがArray[Char]ならあるよ、という場合にはArray#toSeqメソッドで変換して使うことができます。

val Title = "Scala逆引き解説|Scalapedia"
val arr = Array('l', 'a') println(Title.indexOfSlice(arr.toSeq))
3

ScalaのStringindexOfSliceメソッドがあるのはなぜ?

さて、ここでひとつ脱線しますね。

ScalaのStringクラス自体はjava.lang.Stringなので、Javaと共通です。
しかし、JavaのStringにはindexOfSliceメソッドはありません。 なぜJavaのStringに無いはずのメソッドがScalaにおいて生えているのでしょうか?

それは、StringがScalaにおいてWrappedStringに暗黙的に変換されるからです。
このWrappedString型が継承しているSeqOpsクラスにindexOfSliceメソッドが定義されているため、あたかもStringにメソッドが生えているかのように見えるのです。

lastIndexOfSliceで一連の文字を後ろから検索する

lastIndexOfSliceというメソッドもあります。
その名の通り、後ろから検索する以外はindexOfSliceと同じ機能を持つメソッドです。

検索対象となる文字はSeqで指定します。
こちらは逆順になりません。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.lastIndexOfSlice(Seq('l', 'a')))

出力は以下のようになります。
後ろの"la"は15文字目から始まるので、インデックスは14となります。

14

indexWhereで条件を満たす文字の位置を調べる

Scalaでは、indexWhereメソッドを使うと、特定の1文字だけでなく特定の条件を満たすようないずれかの文字を検索することもできます。

文字が満たすべき条件を指定して検索してみましょう。
ここではアルファベットの小文字を検索してみます。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexWhere(_.isLower))

結果は以下のようになります。 先頭から数えて最初の小文字である"c"がヒットし、そのインデックスである1が出力されます。

1

indexWhereで正規表現にマッチする文字列の位置を調べる

また、indexWhereメソッドは正規表現パターンと組み合わせて使うことができます。
これにより、文字が満たすべき条件を正規表現で指定することができます。

こんどは文字列に含まれる数字を検索してみましょう。

val Title = "Scala逆引き解説|Scalapedia"
@annotation.nowarn val result = Title.indexWhere(c => "\\d".r().matches(c.toString) ) println(result)

結果は以下のようになります。
元の文字列には数字が含まれていなかったので-1が出力されます。

-1

lastIndexWhereで条件を満たす文字の位置を後ろから調べる

indexWhereにも後ろから検索するメソッドが用意されており、それがlastIndexWhereメソッドです。

こんどは最後の大文字の位置を取得してみましょう。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.lastIndexWhere(_.isUpper))

うしろの"S"は12番目なので、インデックスは11となります。

11
Scala Standard Library 3.3.1:WrappedString#lastIndexWhere(A=>Boolean):Int

指定した位置以降に出てくる特定の文字列の位置を知りたい場合

これにて、先頭や末尾の文字や文字列の位置を調べることができるようになりました。

それでは、それぞれ2番目以降に出現する文字列の位置を取得するにはどうしたらよいでしょうか?

これまで紹介してきたメソッドには、それぞれ、第2引数に検索を開始する位置を与えられるメソッドが定義されています。
指定した位置以降に出てくる特定の文字列の位置を取得したい場合は、これらのメソッドを使うことができます。

Java
public int indexOf(int ch, int fromIndex)
Scala
// Scala風に読み替えるとこうなります def indexOf(ch: Int, fromIndex: Int): Int

indexOfメソッドを使って、指定した位置以降にある特定の文字を検索してみましょう。
インデックス5(つまり6番目)以降にある文字"a"を検索します。

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexOf('a', 5))

結果は以下のようになります。
インデックス5以降で最初の"a"のインデックスである13が出力されます。

13
Scala Standard Library 3.3.1:WrappedString#indexOfSlice(Seq[B], Int): Int

n回目に出てくる特定の文字・文字列の位置を知りたい場合

1文字の場合

さて、それでは特定の文字がn回目に出てくる位置を調べてみましょう。

新たにindexOfメソッドを定義します。
indexOfメソッドには元の文字列、検索対象の文字、出現回数を渡します。

末尾再帰の最適化をするアノテーションscala.annotation.tailrecは忘れずに付けましょう。

Scala Standard Library 3.3.1:scala.annotation.tailrec
def indexOf(target: String, elem: Char, count: Int): Int = @annotation.tailrec def go(target: String, elem: Int, count: Int, fromIndex: Int): Int = if (count <= 0) -1 else if (count == 1) target.indexOf(elem, fromIndex) else val nextCount = count - 1 val nextIndex = target.indexOf(elem, fromIndex) + 1 go(target, elem, nextCount, nextIndex) go(target, elem.toInt, count, 0)
val Title = "Scala逆引き解説|Scalapedia"
println(indexOf(Title, 'a', -1)) println(indexOf(Title, 'a', 0)) println(indexOf(Title, 'a', 1)) println(indexOf(Title, 'a', 2)) println(indexOf(Title, 'a', 3))

結果は以下のようになります。

-1 -1 2 4 13

文字列の場合

さて、こんどは特定の文字列がn回目に出てくる位置を調べてみましょう。

重ならないように数える場合の方法です。

def indexOf(target: String, elem: String, count: Int): Int = @annotation.tailrec def go(target: String, elem: String, count: Int, fromIndex: Int): Int = if (count <= 0) -1 else if (count == 1) target.indexOf(elem, fromIndex) else val nextCount = count - 1 val nextIndex = target.indexOf(elem, fromIndex) + elem.length go(target, elem, nextCount, nextIndex) go(target, elem, count, 0)
val Title = "Scala逆引き解説|Scalapedia"
println(indexOf(Title, "la", -1)) println(indexOf(Title, "la", 0)) println(indexOf(Title, "la", 1)) println(indexOf(Title, "la", 2))
-1 -1 3 14

可変長文字列(StringBuilder)の場合

可変長文字列(StringBuilder)の場合も、Stringと同様にindexOflastIndexOfindexOfSliceindexWhereメソッドを使用することができます。

indexOfでStringBuilder内の文字を検索する

val Title = "Scala逆引き解説|Scalapedia"
println(TitleBuilder.indexOf('p'))
16
Scala Standard Library 3.3.1:StringBuilder#indexOf[B >: Char](B):Int
Scala Standard Library 3.3.1:StringBuilder#indexOf[B >: Char](B, Int):Int

indexOfでStringBuilder内の文字列を検索する

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexOf("ap"))
15
Scala Standard Library 3.3.1:StringBuilder#indexOf(String):Int
Scala Standard Library 3.3.1:StringBuilder#indexOf(String, Int):Int

lastIndexOfでStringBuilder内の文字を後ろから検索する

val Title = "Scala逆引き解説|Scalapedia"
println(TitleBuilder.lastIndexOf('l'))
14
Scala Standard Library 3.3.1:StringBuilder#lastIndexOf(String):Int
Scala Standard Library 3.3.1:StringBuilder#lastIndexOf(String, Int):Int

indexOfSliceでStringBuilder内の文字列を検索する

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexOfSlice(Seq('l', 'a')))
3
Scala Standard Library 3.3.1:WrappedString#indexOfSlice(that: Seq[B]): Int

lastIndexOfSliceでStringBuilder内の文字列を後ろから検索する

val Title = "Scala逆引き解説|Scalapedia"
println(Title.lastIndexOfSlice(Seq('l', 'a')))
14

indexWhereでStringBuilder内の条件を満たす文字の位置を調べる

val Title = "Scala逆引き解説|Scalapedia"
println(Title.indexWhere(_.isLower))
1

indexWhereでStringBuilder内の正規表現にマッチする文字の位置を調べる

val Title = "Scala逆引き解説|Scalapedia"
@annotation.nowarn val result = TitleBuilder.indexWhere(c => "\\d".r().matches(c.toString) ) println(result)
-1

lastIndexWhereでStringBuilder内条件を満たす文字の位置を後ろから調べる

val Title = "Scala逆引き解説|Scalapedia"
println(Title.lastIndexWhere(_.isUpper))
11
Scala Standard Library 3.3.1:StringBuilder#lastIndexWhere(A=>Boolean):Int

サイト内検索


カテゴリ「文字列処理」の記事

文字列をエスケープしたり復元したりする方法(Apache Commons Text) JavaとScalaのString/StringBuilder/StringBuffer使い分け事情 文字列を分割する方法(split・splitAt・linesIterator・linesWithSeparatorsメソッド) trimメソッドで文字列の前後の空白を除去する 文字列が一致するか比較する方法/大文字・小文字を区別せずに比較する方法 特定の文字の文字コード(コード・ポイント)を取得する replaceメソッドなど、文字列を置換する方法を紹介 文字列の先頭や末尾を、取得したり切り落としたりする方法 containsメソッドでStringに特定の文字列が含まれるか調べる方法 数値を文字列に変換する方法 stripメソッドで文字列の前後の全角空白を除去する 文字列を数値に変換するには?to○○メソッドと注意点について 文字列を辞書的に比較する方法/大文字・小文字を区別せずに比較する方法 文字列が特定の文字列で始まるか・終わるかを調べる方法 【getBytes&size】文字列のバイト長を取得する方法 substringでStringを切り取り、部分文字列を抽出する方法 StringOpsとWrappedStringの違いは? 文字列を連結するには?+演算子やString interpolationの使い方 文字列の大文字へ・小文字へ変換する方法 文字列を逆順にする方法 文字列の文字コード(文字セット)を変換する方法 文字列の長さを取得する方法:lengthとcodePointCountの使い分け 文字列をURLエンコード・デコードする方法 文字列が正規表現に合致するか調べる方法 この文字の位置はどこ?文字列のインデックスを取得する方法 文字列をバイト列に、またはバイト列を文字列に変換する方法

カテゴリ「文字列処理」の記事

文字列をエスケープしたり復元したりする方法(Apache Commons Text) JavaとScalaのString/StringBuilder/StringBuffer使い分け事情 文字列を分割する方法(split・splitAt・linesIterator・linesWithSeparatorsメソッド) trimメソッドで文字列の前後の空白を除去する 文字列が一致するか比較する方法/大文字・小文字を区別せずに比較する方法 特定の文字の文字コード(コード・ポイント)を取得する replaceメソッドなど、文字列を置換する方法を紹介 文字列の先頭や末尾を、取得したり切り落としたりする方法 containsメソッドでStringに特定の文字列が含まれるか調べる方法 数値を文字列に変換する方法 stripメソッドで文字列の前後の全角空白を除去する 文字列を数値に変換するには?to○○メソッドと注意点について 文字列を辞書的に比較する方法/大文字・小文字を区別せずに比較する方法 文字列が特定の文字列で始まるか・終わるかを調べる方法 【getBytes&size】文字列のバイト長を取得する方法 substringでStringを切り取り、部分文字列を抽出する方法 StringOpsとWrappedStringの違いは? 文字列を連結するには?+演算子やString interpolationの使い方 文字列の大文字へ・小文字へ変換する方法 文字列を逆順にする方法 文字列の文字コード(文字セット)を変換する方法 文字列の長さを取得する方法:lengthとcodePointCountの使い分け 文字列をURLエンコード・デコードする方法 文字列が正規表現に合致するか調べる方法 この文字の位置はどこ?文字列のインデックスを取得する方法 文字列をバイト列に、またはバイト列を文字列に変換する方法