Scalaの文字列は、基本的にはjava.lang.Stringを利用している。
但し、PredefでStringOpsへの暗黙の型変換が定義されているので、StringOpsの関数も利用できる。StringOpsはStringLikeを継承している。
また、StringOpsとStringLikeは、scala.collection.immutableパッケージの中(ListとかMapがいるとこ)に入っており、StringLikeはcollectionが持つようないくつかのクラスを継承している。
そのため、collect、distinct、countなどのListなどでお馴染みの関数が利用可能だったり、foreachやforループが使えるなど、文字列に対してCollection的な扱いができるようになっている。
お陰でやたらとたくさんの関数が利用できるようになってるけど、Collection的なものの一部は明らかにStringでは使えないだろ的なものも混ざっているような気がする。
java.lang.StringのJavadoc
http://java.sun.com/javase/ja/6/docs/ja/api/java/lang/String.html
StringOpsのScaladoc
http://www.scala-lang.org/api/current/scala/collection/immutable/StringOps.html
文字列の繰り返しを得る。内部的にはStringBuilderで指定回数分連結している。
scala> "abc" * 3
String = abcabcabc
括弧で指定インデックスのCharを取り出せる。これは動作的にはStringのcharAtと同じになる。
scala> "ABCDE"(2)
Char = C
scala> "ABCDE".charAt(2)
Char = C
// 範囲外のインデックスを指定した場合は、どちらも例外になる。
scala> "ABCDE"(10)
java.lang.StringIndexOutOfBoundsException: String index out of range: 10
大なり小なりで比較できる。
scala> "abc" > "def"
Boolean = false
scala> "abc" < "def"
Boolean = true
「<=」や「>=」も利用可能。
引数で渡されたStringBuilderに、文字列を追加する。
scala> val builder = new StringBuilder()
scala> "abc" addString builder
StringBuilder = StringBuilder(a, b, c)
引数に開始文字列、区切り文字、終了文字列なども指定できるらしい。
scala> ( "abc" addString(builder, "[", ",", "]") ) toString
java.lang.String = [a,b,c]
文字列の先頭を大文字にする。先頭が英字でなければそのままの値を返す。
scala> "mac" capitalize
String = Mac
全体を大文字に、もしくは全体を小文字にする場合は、java.lang.StringのtoUpperCaseや.toLowerCaseを利用する。
1文字ずつcase文で処理できる。
// AとCだけ大文字に変換する
scala> "mac" collect { case 'a' => 'A'; case 'c' => 'C'; case x => x }
String = mAC
文字列を比較する。compareはStringLikeで実装されているが、中身はjava.lang.StringのcompareToを呼び出しているだけ。
scala> "linux" compare "windows"
Int = -11
scala> "linux" compareTo "windows"
Int = -11
java.lang.Stringの機能。引数で渡した文字列が含まれる場合はtrue。
scala> "windows" contains "dow"
Boolean = true
指定した条件が当てはまる文字をカウント。
// 「w」の出現回数をカウント
scala> "windows" count { _ == 'w' }
Int = 2
// アルファベット大文字の数をカウント
scala> "AbcDeF" count { c => c >= 'A' && c <= 'Z' }
Int = 3
差分を出す。対象となるのは左辺のみ。たとえば「abc」と「bcd」のdiffは、「abc」の中で右辺に含まれてない文字=「a」になる。右辺の中で左辺と一致しない「d」は抽出されない。
scala> "abc" diff "bcd"
String = a
scala> "aabbcc" diff "abbc"
String = ac
文字の重複を省く。
scala> "ABCABDACBADB" distinct
String = ABCD
先頭から指定した文字だけ落とす。
scala> "Windows" drop 3
String = dows
右側から指定した文字だけ落とす。
scala> "Windows" dropRight 3
String = Wind
条件に合致している間、文字をdropする。
// 「d」が出現するまでdrop
scala> "Windows" dropWhile {_ != 'd'}
String = dows
java.lang.Stringの機能。末尾が指定した文字列で終わっているか。
scala> "linux" endsWith "ux"
Boolean = true
scala> "linux" endsWith "ws"
Boolean = false
両方ともjava.lang.Stringの機能。equalsは==と同様の挙動になる。equalsIgnoreCaseは名前の通り、大文字小文字を無視する。
scala> "linux" equals "LINUX"
Boolean = false
scala> "linux" equalsIgnoreCase "LINUX"
Boolean = true
指定した条件に当てはまる文字が存在するか。
// 文字列の中に「x」が存在するかチェック
scala> "linux" exists {_ == 'x'}
Boolean = true
filterは指定した条件に当てはまらない文字を落とす。filterNotはその逆。
// 大文字小文字を無視して、A〜F以外は落とす
scala> "Alphabet" filter {c => c.toUpper >= 'A' && c.toUpper <= 'F'}
String = Aabe
// 逆にA〜Fを落とす
scala> "Alphabet" filterNot {c => c.toUpper >= 'A' && c.toUpper <= 'F'}
String = lpht
指定した条件に当てはまる文字を見つける。結果は、findは最初に見つけた文字(Option型)、findIndexOfは最初に見つけた文字の場所をIntで返す。
// Kより小さい最初の文字
"windows" find (_.toUpper < 'K')
Option[Char] = Some(i)
// findIndexOfは「i」の場所(1)を返す
scala> "windows" findIndexOf (_.toUpper < 'K')
Int = 1
すべての文字に対して条件が当てはまればtrue、1文字でも条件外の文字があればfalse。
// 全文字が数値かチェック
scala> "1024" forall { _.isDigit }
Boolean = true
// 数値以外の文字が入っていたらfalse
scala> "10.5" forall { _.isDigit }
Boolean = false
すべての文字に対して処理を実行。
// 1文字ずつずらして表示してみる
scala> "windows" foreach { c => print( ( c + 1 ).toChar ) }
// 余談だけどfor式も使える
scala> for( c <- "windows" ) print(c.toUpper)
WINDOWS
Cのprintfに似たフォーマットを利用できる。内部的にはjava.lang.Stringのformatを利用。
scala> "%d/%d/%d(%s)" format (2011, 5, 5, "Thu")
String = 2011/5/5(Thu)
formatLocalは第一引数にLocaleを取る。これも動作はjava.lang.Stringのformatと同じ。
scala> val cal = java.util.Calendar.getInstance
scala> "%tA" formatLocal (java.util.Locale.US, cal)
String = Thursday
scala> "%tA" formatLocal (java.util.Locale.JAPAN, cal)
String = 木曜日
フォーマット文字列の詳細はjava.util.Formatter参照。
http://java.sun.com/javase/ja/6/docs/ja/api/java/util/Formatter.html
グループ分けしたMapを返す。
// 大文字小文字問わず、同じ文字でグループ分けしてみる
scala> "ABbCcc" groupBy (x => x.toLower)
scala.collection.immutable.Map[Char,String] = Map((a,A), (c,Ccc), (b,Bb))
指定文字数で分割したiteratorを返す
// 3文字区切り
( "windows" grouped 3 ) foreach(println)
win
dow
s
先頭の文字を取得する。
scala> "linux" head
Char = l
// 空文字に実行すると例外
scala> "" head
java.util.NoSuchElementException
// headOptionなら、空文字でも例外が起きずにOption型で返る
scala> "" headOption
Option[Char] = None
// 文字がある場合はもちろんSome(文字)が返る
scala> "linux" headOption
Option[Char] = Some(l)
indexOfは指定したCharが最初に出現する場所を返す。indexWhereは条件に当てはまる最初の文字の場所を返す。
// 最初に出てくる「d」の場所
scala> "windows" indexOf 'd'
Int = 3
// 3文字目以降の「w」の場所
scala> "windows" indexOf('w', 3)
Int = 5
// 存在しない場合は-1
scala> "windows" indexOf 'l'
Int = 5
// indexWhereで、「f」より小さい最初の文字を指定
scala> "windows" indexWhere( _ < 'f' )
res177: Int = 3
// 3文字目以降で「o」より大きい最初の文字を指定
scala> "windows" indexWhere( _ > 'o', 3 )
res178: Int = 5
文字数に応じたRangeを返す。
scala> "linux" indices
scala.collection.immutable.Range = Range(0, 1, 2, 3, 4)
最後の1文字を除いた文字列を返す。
scala> "linux" init
String = linu
diffの逆。一致する部分だけ返す。
scala> "aabbccdd" intersect "cdef"
String = cd
指定したindexが存在するかチェックする。内部的にやってることは「(idx >= 0) && (idx < length)」。
// 3文字目は存在するからtrue
scala> "linux" isDefinedAt 3
Boolean = true
// 10文字目は存在しないからfalse
scala> "linux" isDefinedAt 10
Boolean = false
中身が空じゃないかチェックする。
// 空文字ならtrue
scala> "" isEmpty
Boolean = true
// nullだとお馴染みの例外
scala> (null: String) isEmpty
java.lang.NullPointerException
// 逆の結果を返すnonEmptyもある
scala> "" nonEmpty
Boolean = false
Iterator[Char]を取得する。Scalaの文字列はItrableLikeを継承してる。
scala> "linux" iterator
Iterator[Char] = non-empty iterator
最後の文字を取得する。
scala> "linux" last
Char = x
// 空文字に実行すると例外
scala> "" last
java.util.NoSuchElementException
// OptionでラップするlastOptionを使えば安全
scala> "" lastOption
Option[Char] = None
後方から検索する以外はindexOfと同じ。
// 最後に出てくる「w」の位置
scala> "windows" lastIndexOf 'w'
Int = 5
// 最後に出てくる「o」より小さい文字の位置
scala> "windows" lastIndexWhere(_ < 'o')
Int = 3
文字数を返す。sizeも同じ。
scala> "linux" size
Int = 5
引数で指定した数値とlengthとの差を出す。
scala> "linux" lengthCompare 3
Int = 2
scala> "linux" lengthCompare 5
Int = 0
scala> "linux" lengthCompare 7
Int = -2
文字列を改行で区切ってIteratorを返す。linesとlinesIteratorは同じ処理をしているように見える。
linesとlinesIteratorは、区切った改行を取り去る。linesWithSeparatorsは改行を残す。
// 改行で区切る
scala> ( "one\ntwo\nthree" lines ) foreach println
one
two
three
// linesWithSeparatorsだと、改行が残っている
scala> ( "one\ntwo\nthree" linesWithSeparators ) foreach println
one
two
three
Listなどで利用されるあのmap。Collectionを回して処理を加え、新しいCollectionを生成する。
// 文字を1文字ずつずらした文字列を作成する
scala> "windows" map { c => (c + 1).toChar }
String = xjoepxt
// 大文字は小文字に、小文字は大文字に変換する
scala> "Windows" map( c => if(c.isUpper) c.toLower else c.toUpper )
String = wINDOWS
java.lang.Stringの機能。正規表現に適合するかをチェックする。
scala> "linux" matches "[a-z]+"
Boolean = true
文字コード的に最大、最小の文字を抽出する。
scala> "Linux" max
Char = x
// 大文字の方が小文字よりコード的に小さいので、「L」が最小になる
scala> "Linux" min
Char = L
// 大文字小文字を無視するOrderを作ってみる
scala> val order = new Ordering[Char]() {
| def compare(x: Char, y: Char) = x.toLower compare y.toLower
| }
// 上記のOrderingを引数に指定すると、「i」が最小になる
scala> "Linux" min order
Char = i
Listとかで連結して文字列を作りたい時に使うmkString。Stringクラスで使うことはあまりないと思われる。
// そのまま指定しても意味なし
scala> "Mac" mkString
String = Mac
// seqを指定して分割された感じにしてみる
scala> "Mac" mkString " | "
String = M | a | c
// 前と後ろに括弧を入れてみる
scala> "Mac" mkString("(", "", ")")
String = (Mac)
指定された文字数まで、指定文字で穴埋めする。
scala> "Mac" padTo (6, '_')
String = Mac___
指定した条件で文字列を分ける。戻り値はTuple2の(String, String)。
// 大文字と小文字で分けてみる
scala> "AaBbCc" partition( _.isUpper )
(String, String) = (ABC,abc)
パッチを当てる。
// 一番最初の文字を「L」に書き換える
scala> "Windows" patch(0, "L", 1)
String = Lindows
// 一番最初の2文字を「LinLi」に書き換える
scala> "Windows" patch(0, "LinLi", 2)
String = LinLindows
// 2文字目からの2文字を伏字にする
scala> "Fuck" patch(2, "xx", 2)
res24: String = Fuxx
前方から条件に一致しない文字が出るまでカウントする。
// スペースが出てる間、カウント
scala> " Test" prefixLength(_ == ' ')
Int = 3
「r」1文字でscala.util.matching.Regexのインスタンスが取れる。正規表現の準備が楽になりそう。
scala> "([0-9]{4})" r
scala.util.matching.Regex = ([0-9]{4})
// 置換してみる
scala> ( "([0-9]{4})" r ) replaceFirstIn("2011/5/5", "xxxx")
String = xxxx/5/5
reduceも用意されているのだけど、良い用途が思いつかない。
// とりあえず強引に文字を足してみるとか意味のないことをしてみる
scala> "abc" reduceLeft((x, y) => (x.toInt + y.toInt).toChar)
java.lang.Stringの機能。文字列を置換する。
scala> "JavaJava".replaceAll("Java", "Scala")
java.lang.String = ScalaScala
scala> "JavaJava".replaceFirst("Java", "Scala")
java.lang.String = ScalaJava
StringOpsからラップしているStringに触りたい時などに内部的に使っている。これ、protectedじゃいけないのだろうか。
// StringOpsを利用
scala> val mac = new scala.collection.immutable.StringOps("Mac")
scala.collection.immutable.StringOps = Mac
// reprでStringクラスが取れる
scala> mac.repr
String = Mac
普通はtoStringしそうな気もする。いや、普通はStringOpsを明示的に使うことなんてないか。
文字列を逆順にする。
scala> "Windows" reverse
String = swodniW
文字列を逆順に回すIteratorを取得する
scala> ("Windows" reverseIterator) foreach print
swodniW
mapのreverse版。
// 逆さまにして大文字に変換してみる
scala> "Linux" reverseMap(_.toUpper)
String = XUNIL
Iterableなものと中身が同じかどうか比較する。
// Listと比較しても、同じものが入ってればtrue
scala> "Mac" sameElements List('M', 'a', 'c')
Boolean = true
// 順序も同じでないといけない
scala> "Mca" sameElements List('M', 'a', 'c')
Boolean = false
scanLeftは左から右に、scanRightは右から左に結果を持ち越しながら処理を実行する。
// こんな風に書くものだけど、Stringでの用途が思いつかない
scala> (1 to 5).scanLeft(1){_ + _}
scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 4, 7, 11, 16)
prefixLengthが内部的に利用しているもの。指定したインデックスから条件に合う文字が連続する数を拾ってきてくれる。
// 3文字目以降で大文字が連続する数
scala> "xxxXXXxxx" segmentLength(_.isUpper, 3)
Int = 3
lengthと同じ。文字数をカウントする。
部分文字列を取り出す。java.lang.Stringのsubstringは文字列より大きいレンジを指定すると例外が起きたが、sliceは範囲より大きい値が指定された場合は文字列の終端まで取得するなど配慮してくれている。
// 文字数を超える値を指定しても大丈夫
scala> "Windows" slice(3, 10)
String = dows
// substringでは例外
scala> "Windows" substring(3, 10)
java.lang.StringIndexOutOfBoundsException: String index out of range: 10
// -1やstartとendが違う値を入れるなどした場合は、空文字が返る
scala> "Windows" slice(-1, -5)
res114: String =
指定した文字数でスライドしながらiteratorを作る。
// 3を指定すると、1文字ずつスライドしながら3文字区切りのIteratorを返す
scala> "Windows".sliding(3).toList
List[String] = List(Win, ind, ndo, dow, ows)
// デフォルトのstepは1
scala> "Windows".sliding(3, 1).toList
List[String] = List(Win, ind, ndo, dow, ows)
// stepが2になると、2個ずつスライドする
scala> "Windows".sliding(3, 2).toList
List[String] = List(Win, ndo, ows)
条件でソートする
scala> "Wimax" sorted
res11: String = Waimx
scala> "Wimax" sortBy(c => c)
String = Waimx
scala> "Wimax" sortWith(_ > _)
String = xmiaW
条件に当てはまる間の値と、それ以降の値とで分割する。
// 大文字である間の文字列と、それ以降の文字列で分割
scala> "WinWin".span(_.isUpper)
(String, String) = (W,inWin)
文字列を分割する。java.lang.Stringには正規表現を使って分割するsplit関数がいる。
ScalaはChar、もしくはArray[Char]でセパレータを指定して分割するsplit関数がいる。
// Java版
scala> "2011/5/5 10:22:33" split "[\\/\\:%s]"
Array[java.lang.String] = Array(2011, 5, 5 10, 22, 33)
// Scala版(Char指定)
scala> "2011/5/5" split '/'
Array[String] = Array(2011, 5, 5)
// Scala版(Array[Char]指定)
scala> "2011/5/5 10:22:33" split Array('/', ':', ' ')
Array[String] = Array(2011, 5, 5, 10, 22, 33)
指定したposで文字列を分割する。
scala> "Firefox" splitAt 3
(String, String) = (Fir,efox)
文字列の始端が指定された文字列になっているか確認する。
scala> "Firefox" startsWith "Fire"
Boolean = true
// 第2引数に数値を入れると、その場所からの文字列で評価
scala> "Firefox" startsWith("fox", 4)
Boolean = true
文字列の末尾の改行を削除する
scala> "ONE\n".stripLineEnd
Scalaの文字列はダブルコーテーションを3つ繋げると、複数行にわたって記述できる。
scala> val str = """Firefox
| Opera
| Chrome"""
str: java.lang.String =
Firefox
Opera
Chrome
しかし、上記のように書くとインデントがイマイチ。それを解決するのが、stripMarginらしい。デフォルトでは「|」をmarginCharに設定して、それが出現した以降の文字列のみ有効にしてくれる。
scala> val str = """Firefox
| |Opera
| |Chrome""".stripMargin
str: String =
Firefox
Opera
Chrome
「|」以外にも任意のmarginCharを指定可能。以下は「%」を指定した場合。
scala> val str = """Firefox
| %Opera
| %Chrome""".stripMargin('%')
str: String =
Firefox
Opera
Chrome
指定した文字列で開始していたら(内部的にはstartsWithで判定)、その指定文字部分を取り除く。
// Opera - Ope = ra
scala> "Opera" stripPrefix "Ope"
java.lang.String = ra
// 開始文字列が一致しなければ、そのままの文字列を返す
scala> "Opera" stripPrefix "IE"
java.lang.String = Opera
stripPrefixの逆版。こちらはendsWithで判定。
cala> "Opera" stripSuffix "ra"
java.lang.String = Ope
文字をコード的な数字にして合算する。Collection的なものには付いてくる機能。たぶん文字列でこれは使わない。
scala> "abc" sum
//=> 良く分からない文字が出力される
1つ目の要素を除いて、それ以外の要素を取得する関数。
scala> "chrome" tail
String = hrome
takeは前方から指定文字数分取得。takeRightは後方から指定文字数分取得。
takeWhileは条件に合致している間の文字列を取得。
// take
scala> "chrome" take 3
String = chr
// takeRight
scala> "chrome" takeRight
String = ome
// takeWhile(oが出現するまで)
scala> "chrome" takeWhile(_ != 'o')
String = chr
Char配列を取得する。toCharArrayはjava.lang.Stringの同様の機能。
scala> "ie" toArray
Array[Char] = Array(i, e)
scala> "ie" toCharArray
Array[Char] = Array(i, e)
他の型への変換。
scala> "10" toInt
Int = 10
scala> "10" toDouble
res71: Double = 10.0
// 数値でないものを変換すると例外
scala> "foo" toDouble
java.lang.NumberFormatException: For input string: "foo"
// toBooleanは文字列がtrueもしくはfalse(Ignore Case)の場合に変換可能
scala> "True" toBoolean
Boolean = true
// true/false以外をtoBooleanすると例外
scala> "Trururu" toBoolean
java.lang.NumberFormatException: For input string: "Trururu"
ScalaのCollection的な機能に変換できる。
scala> "mono" toList
List[Char] = List(m, o, n, o)
scala> "mono" toBuffer
scala.collection.mutable.Buffer[Char] = ArrayBuffer(m, o, n, o)
java.lang.Stringの機能。大文字、小文字変換。
scala> "Linux" toUpperCase
java.lang.String = LINUX
scala> "WINDOWS" toLowerCase
java.lang.String = windows
java.lang.Stringの機能。前方と後方の空白文字を除去する。
scala> "\ntsu ka re ta \n" trim
java.lang.String = tsu ka re ta
フィルタをかけた上で、mapとかforeachを実行できる。
// 「o」より小さい物だけ取り出す
scala> "Windows".withFilter(_ < 'o').map(c => c)
String = Wind
2つの文字列の同じ位置をペアにする。zipは長さが一致しない場合は、短い方に合わせる。
zipAllは長さが一致しない場合に、残りを指定した値で埋める。2つ目の引数がthis側、3つ目の引数がthat側を埋める文字。
scala> "1567" zip "2482"
scala.collection.immutable.IndexedSeq[(Char, Char)] = Vector((1,2), (5,4), (6,8), (7,2))
// 長さが一致しない場合
scala> "1567" zip "248"
scala.collection.immutable.IndexedSeq[(Char, Char)] = Vector((1,2), (5,4), (6,8))
// zipAll
scala> "1568".zipAll("24", '0', '5')
res105: scala.collection.immutable.IndexedSeq[(Char, Char)] = Vector((1,2), (5,4), (6,5), (8,5))