ふと、この記事にあるようなこと、Scalaだったらどう書くかな?と思ってまとめてみました。途中で力尽きたので、まずは前半戦まで!
www.m3tech.blog
あの処理、Scalaでどう書く?
基本的にScalaの標準ライブラリでやる方法を紹介し、Javaの標準ライブラリを使う必要があるところはその旨書いてあります。
また、調べていくとScalaとJavaの標準ライブラリではできないことも有ったので、そこには「標準ライブラリではできません」と書いておきます。標準ライブラリ以外でやれる方法が有ったら、ぜひ教えてください。
なお、標準ライブラリはScala 2.12、Java 11の機能に基づいています。
どちらもScalaのscala.Console
オブジェクトのprintln
メソッドを使います(パッケージ名のscalaは省略可なので、以降は省略します)。
println("HELLO")
Console.err.println("ERROR!")
単にprintln
と書いた場合は、Console.out.println
が呼び出されます。
なお、標準ライブラリにはログ出力ライブラリは用意されていません。
ファイル関係
基本的にScalaの標準ライブラリでは、ファイル関係のサポートがほぼ無いので、基本的にJavaの標準ライブラリを使います。
パスの操作
Java 7から追加されたjava.nio.file.Paths
クラスを使います。
import java.nio.file.Paths
val p = Paths.get("/foo/bar/baz.txt")
p.getFileName
p.getParent
val dirpath = Paths.get("/foo/bar")
dirpath.resolve("baz.txt")
Path
は、toString
で文字列に戻せます。また、Java6以前に作られたライブラリはjava.io.File
クラスを要求するものがありますが、toFile
でjava.io.File
に変換できます。
import java.nio.file.Paths
val fullpath = Paths.get("/foo/bar/baz.txt")
fullpath.toString
fullpath.toFile
ScalaやJavaでチルダや環境変数をパスに展開してくれる標準ライブラリは無いようです。
実現しているライブラリがあればぜひ教えてください。
ファイルの読み書き
テキストデータの読み込みにはScalaのscala.io.Source
オブジェクトのfromFile
メソッドが便利です。
val source = io.Source.fromFile("foo.txt", "utf-8")
val lines = source.lines
lines.foreach(println)
source.close
fromFile
メソッドは文字列でのパス名以外にもjava.io.File
クラスのオブジェクトも指定可能です。また、scala.io.Source
はデフォルトで文字コードとして"UTF-8"が使われるようになっていますが、明示的に指定した方が分かりやすいでしょう。
反対に書き込みについては、専用のライブラリは(なぜか)用意されていないので、Java 7で導入されたFiles.newBufferedReader
メソッドを使います。ただし、Scalaにはtry-with-resource
構文が無いので自前でクローズします。
import java.nio.file.Paths
import java.nio.file.Files
import java.nio.charset.StandardCharsets
val path = Paths.get("foo.txt")
val writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)
try {
writer.append("内容")
writer.newLine
} finally {
writer.close
}
構造が簡単なうちは自前でclose
メソッドを呼び出すようなコードを書いていても大丈夫だと思いますが、コードの構造が大規模になってくるとclose
の呼び出しも複雑化してきます。このような場合、Javaであればtry-with-resource
構文がありますし、C#にはusing
構文があります。Scalaでも構文は用意されていませんが、コーディングテクニックとしての「ローンパターン(loan pattern)」と呼ばれるものがあります。ここでは説明は割愛しますが、調べてみると色々な書き方が有るようなので、試してみましょう。
行数を数える(wc -l)
特に専用の構文やライブラリが用意されているわけではないですが、先ほどのio.Source
を使うとシンプルに書けます。
val source = io.Source.fromFile("foo.txt")
source.getLines.length
ファイルの列挙
Java7で導入されたFiles.newDirectoryStream
を使います。
import java.nio.file.Paths
import java.nio.file.Files
val files = Files.newDirectoryStream(Paths.get("."), "*.txt")
try {
files.forEach(println)
} finally {
files.close
}
ファイルの情報(存在確認・作成日時)
java.nio.file.Files
のメソッド群を使います。
import java.nio.file.Paths
import java.nio.file.Files
val f = Paths.get("foo.txt")
Files.exists(f)
Files.isRegularFile(f)
Files.isDirectory(f)
Files.getLastModifiedTime(f)
その他、ファイルシステム固有の属性(作成日時、アクセス日時)は、Files.getAttribute
メソッドを使います。戻り値がobject
型になってしまうので、キャストが必要な点が要注意です。
val createTime = Files.getAttribute(Paths.get("foo.txt"), "unix:creationTime").asInstanceOf[java.nio.file.attribute.FileTime]
val accessTime = Files.getAttribute(Paths.get("foo.txt"), "unix:lastAccessTime").asInstanceOf[java.nio.file.attribute.FileTime]
コピー・移動・削除
こちらもjava.nio.file.Files
のメソッド群を使います。
import java.nio.file.Paths
import java.nio.file.Files
Files.copy(Paths.get("foo.txt"), Paths.get("bar.txt"))
Files.delete(Paths.get("bar.txt"))
Files.move(Paths.get("foo.txt"), Paths.get("bar.txt"))
コピーや、移動はオプションとしてStandardCopyOption
が用意する定数を指定することができます。例えば、StandardCopyOption.REPLACE_EXISTING
を指定すると、すでにファイルが有っても上書きします。
import java.nio.file.Paths
import java.nio.file.Files
import java.nio.file.StandardCopyOption
Files.copy(Paths.get("foo.txt"), Paths.get("bar.txt"))
Files.copy(Paths.get("foo.txt"), Paths.get("bar.txt"), StandardCopyOption.REPLACE_EXISTING)
と、ここまでで力尽きたので、続きは明日以降!
外部コマンド
単純に実行する
外部のコマンドを実行し、標準出力を受け取る:
リダイレクトを使う
パイプを使う
spawn → wait (外部コマンドを起動し、終了を待つ)
シェルを実行する【危険!!】
時刻関係
文字列関係
文字列への式埋め込み
ヒアドキュメント
終了時の処理&シグナルをtrapする
吉祥寺.pmへの参加者を募集しています
このブログは企業テックブログという訳でもないので、特にエンジニア募集とかないですけど、定期的に吉祥寺.pmというイベントをやっているので、良かったら参加してみてください。pmとはついていますが、Scalaのトークも歓迎です!
kichijojipm.connpass.com
あと、良かったら、Twitterのアカウントもフォローしてください。設計のこととかツイートしています。
twitter.com