Julia - 標準入力・出力・エラー出力先を変える

環境

  • OS: ArchLinux
  • Julia 1.0.1

入出力先を変える

Python だと stdout を気軽に上書きして標準出力先をファイルにできますが、Julia の場合はそれができません。

julia> f = open("out.txt", "w")
IOStream(<file out.txt>)

julia> stdout = f
ERROR: cannot assign variable Base.stdout from module Main
Stacktrace:
 [1] top-level scope at none:0

標準出力先を変えたい時は redirect_stdout を使って変更します。

julia> originalstdout = stdout # 元の設定を避難しておく
Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting)

julia> stdout
Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting)

julia> (rd, wr) = redirect_stdout()
(Base.PipeEndpoint(RawFD(0x00000013) open, 0 bytes waiting), Base.PipeEndpoint(RawFD(0x00000014) open, 0 bytes waiting))

julia> stdout # 指しているところが wr と同じところになった。
Base.PipeEndpoint(RawFD(0x00000014) open, 0 bytes waiting)

julia> println("Hello") # print を書いても画面には表示されない

julia> println("World")

julia> close(wr)

julia> data = readavailable(rd) # バッファからデータを取ってくる
12-element Array{UInt8,1}:
 0x48
 0x65
 0x6c
 0x6c
 0x6f
 0x0a
 0x57
 0x6f
 0x72
 0x6c
 0x64
 0x0a

julia> close(rd)

julia> redirect_stdout(originalstdout) # 標準出力を元に戻す
Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting)

julia> print(String(data))
Hello
World

標準出力先をファイルに変えることも出来ます。

julia> originalstdout = stdout
Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting)

julia> f = open("out.txt", "w")
IOStream(<file out.txt>)

julia> redirect_stdout(f)
IOStream(<file out.txt>)

julia> println("Hello World")

julia> close(f)

julia> redirect_stdout(originalstdout)
Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting)

shell> cat out.txt
Hello World

標準入力、標準エラー出力についても同じ様に出来ます。

標準入力

shell> cat in.txt
2 4

julia> originalstdin = stdin
Base.TTY(RawFD(0x0000000a) paused, 0 bytes waiting)

julia> f = open("in.txt", "r")
IOStream(<file in.txt>)

julia> redirect_stdin(f)
IOStream(<file in.txt>)

julia> n, m = parse.(Int, split(readline())) # キーボードから入力せずとも勝手にファイルから読み込んでくる
2-element Array{Int64,1}:
 2
 4

julia> println(n * m)
8

julia> close(f)

julia> redirect_stdin(originalstdin)
Base.TTY(RawFD(0x0000000a) paused, 0 bytes waiting)

標準エラー

julia> originalstderr = stderr
Base.TTY(RawFD(0x0000000f) open, 0 bytes waiting)

julia> f = open("out.txt", "w")
IOStream(<file out.txt>)

julia> redirect_stderr(f)
IOStream(<file out.txt>)

julia> println(stderr, "STDERR")

julia> close(f)

julia> redirect_stderr(originalstderr)
Base.TTY(RawFD(0x0000000f) open, 0 bytes waiting)

shell> cat out.txt
STDERR

Julia のソースコードを見る限り、元の標準出力等に戻すには上記の originalstdout のように自力で避難しておくしか方法はなさそうです。ちょっと不便。

使いどころ(?)

SHELL で

$ julia ex.jl < in.txt > out.txt

として動かしていたプログラムを run を使わずに Julia から実行できるようになります。

in.txt

2 4

gcd.jl

x, y = parse.(Int, split(readline()))
mygcd(x,y) = y == 0 ? x : mygcd(y, mod(x,y))
println(mygcd(x,y))

プログラムを実行するための関数

function run_file(input_filename::String, output_filename::String, filename::String)
    originalstdin = stdin
    originalstdout = stdout
    fin = open(input_filename, "r")
    redirect_stdin(fin)
    fout = open(output_filename, "w")
    redirect_stdout(fout)

    include(filename)

    close(fin)
    close(fout)
    redirect_stdout(originalstdout)
    redirect_stdin(originalstdin)
end
julia> @time run(pipeline(`julia gcd.jl`, stdin="in.txt", stdout="out.txt"));
  0.666613 seconds (394.68 k allocations: 20.278 MiB)

julia> @time run(pipeline(`julia gcd.jl`, stdin="in.txt", stdout="out.txt"));
  0.406997 seconds (65 allocations: 2.656 KiB)

julia> @time run(pipeline(`julia gcd.jl`, stdin="in.txt", stdout="out.txt"));
  0.413124 seconds (65 allocations: 2.656 KiB)

julia> @time run_file("in.txt", "out.txt", "gcd.jl");
  0.239453 seconds (392.67 k allocations: 20.973 MiB)

julia> @time run_file("in.txt", "out.txt", "gcd.jl");
  0.031852 seconds (18.40 k allocations: 972.514 KiB, 20.19% gc time)

julia> @time run_file("in.txt", "out.txt", "gcd.jl");
  0.026645 seconds (18.40 k allocations: 972.482 KiB)

run を使った方法よりも実行時間は大分改善されます。(メモリは大分消費するようですが。)

競技プログラミングでの Julia の実行時間の計測方法の改善に応用できないかなと思ったのですが、システムを構築するのがとても面倒そうだなと思いました。

参考