LoginSignup
3
3

More than 5 years have passed since last update.

Scalaで正規表現とパターンマッチを使って文字列を抽出して操作する

Posted at

やりたいこと:YouTubeのビデオの長さを秒で取得したい

YouTubeのAPIではビデオの長さが"PT2H7M56S"のような形式で返されます。
最初の"PT"は固定で、残りが"時H分M秒S"のような表現になります。"PT3M12S"のように時間が無い場合もあります。
このままでは扱いにくので秒に変換します。Scalaでは正規表現とパターンマッチの組み合わせで文字列の抽出がいい感じにできます。使ってコードは以下のようになります。

val s = "PT2H7M56S"
val youtubeDurationPattern = "PT(([0-9])+H)?(([0-9])+M)?(([0-9])+S)?".r
val duration = s match {
  case youtubeDurationPattern(_, h, _, m, _, s) => { 
    Some((Option(h).map(_.toInt * 60 * 60) ++ Option(m).map(_.toInt * 60) ++ Option(s).map(_.toInt)).sum)
  }
}
// duration: Some[Int] = Some(7626)

説明

caseの中を細かく分けて型を明示して書くと以下のようになります。

// 正規表現に一致しなければNoneが返る
case youtubeDurationPattern(_, h, _, m, _, s) => { 
  // 抽出した文字列がnullならNone、null以外ならmapの中身が実行されてSome[Int]に変換
  val hd:Option[Int] = Option(h).map(_.toInt * 60 * 60)
  val md:Option[Int] = Option(m).map(_.toInt * 60)
  val sd:Option[Int] = Option(s).map(_.toInt)

  // ++演算子で結合してリストを作成。Noneな結果はリストには含まれない。
  val all:Seq[Int] = hd ++ md ++ sd

  // 結果をsumで合計する
  val totalSec:Int = all.sum
  Some(totalSec)
}

合計用の変数を用意して個別に足し込んでいくのではなく、時、分、秒をOption&リスト化しておき、中身を気にせずガッと合計しているところが関数型プログラミングっぽい気がします。
一応コメントを細かく書いていますが、コードと同じことをコメント化しているだけですね。このように直感的なコードが書けるのがScalaの良いところですね。
もっと良い方法ある気もするので、みなさまのツッコミお待ちしています。

caseの結果がOptionだったらもっと嬉しかった?

caseの結果、抽出した文字列はnullになる可能性があります。nullじゃなくてOption[String]が返るような仕様だったら更に安全ですが、意図があってこのような仕様になっている気もしました。

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