第12回:テキストファイルの入出力
▶ テキストファイルの作成・書き込み
この節では,テキストファイルを作成し, テキストを書き込む方法を説明する. 一般のテキストファイルの拡張子は,txt
である.
テキストファイルを作成するには,関数 open
と close
を組で用いる.
関数 open( fn, "w")
は, ファイル名 fn
のファイルを作成し, テキストを書き込む準備を行う. ファイル fn
は,存在していてもよい.その場合は,元に書き込まれた内容は廃棄される.ファイルが作成できない場合には例外が発生する.
関数 open
は,ファイル記述子 f
を返す. 関数 print(f, s)
は,ファイル記述子 f
で指定されたファイルに文字列 s
を書き込む. 関数 println(f, s)
は,ファイル記述子 f
で指定されたファイルに文字列 s
を書き込んだ後に開業する.
書き込みが終わったら,関数 close(f)
を呼び出し,書き込みを終了する.
f = open("hello1.txt", "w")
print(f, "Hello")
println(f, " again.")
close(f)
上のプログラムを実行したら,Windows のエクスプローラを用いて,ファイル hello1.txt
が作成されることを確かめよ. Windowsの「メモ帳」アプリなどを用いて hello1.txt
を開き,その通りのテキストが書き込まれていることを観察・確認せよ. (※ MacOSの場合は,「ファインダー」アプリと,「テキスト・エディット」アプリに読み替えよ)
上と同じ処理は,do...end
構文を用いて書くこともできる. end
が終わると関数 close(f)
が暗黙的に呼ばれる.
open("hello1.txt", "w") do f
print(f, "Hello")
println(f, " again.")
end
▶ 書式付き文字列と出力
書式を指定して,値を文字列に変換する場合には @sprintf()
マクロを用いる. また,書式を指定して値を印字するには @printf()
マクロを用いる. @sprintf
,@printf
はどちらも, Printf
パッケージに定義されている. using Printf
で,このパッケージを読み込んでから用いる.
マクロ @sprintf(fmt,x)
は, x
を 書式 fmt
に従って文字列にする. マクロ @printf(fmt, x)
は,その文字列を印字する. 書式文字列 fmt
は, C言語の printf
関数などで用いられる書式と,ほぼ同じである.
なお,Jupyter notebookでは,結果が右寄せされて表示されない場合がある.
書式文字列 %i
は,整数を印字する.
i
の前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.指定されたフィールドサイズで収まらない場合には,幅を増やして印字される.- フィールドサイズを
0
で始めると,空白が0
で埋められる. - フィールドサイズの前に
+
を付けると,非負の数の前に+
符号がつく.
julia> using Printf
julia> @printf("%5i\n", -41)
-41
julia> @printf("%05i\n", 413)
00413
julia> @printf("%+5i\n", 413)
+413
julia> @printf("%+5i\n", -413)
-413
julia> @printf("%+05i\n", -413)
-0413
julia> @printf("%+5i\n", -41131)
-41131
書式文字列 %f
は,小数を印字する.
f
の前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.指定されたフィールドサイズで収まらない場合には,幅を増やして印字される.- フォールドサイズに続けて,ピリオド
.
と整数を付けると,小数点以下の桁数を指定する.指定された小数点以下桁数で表されない場合には,丸められる. - フィールドサイズを
0
で始めると,頭の空白が0
で埋められる. - フィールドサイズの前に
+
を付けると,非負の数の前に+
符号がつく. Inf
とNaN
は,その通り印字される.
julia> @printf("%8.3f\n", -2e-2)
-0.020
julia> @printf("%08.0f\n", -0.252)
-0000000
julia> @printf("%8.1f\n", -0.252)
-0.3
julia> @printf("%08.2f\n", -0.252)
-0000.25
julia> @printf("%8.3f\n", -0.252)
-0.252
書式文字列 %e
は,指数形式で印字する.
e
の前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.フィールドサイズで収まらない場合には,幅を増やして印字される.- フォールドサイズに続けて,ピリオド
.
と整数を付けると,小数点以下の桁数を指定する.指定された小数点以下桁数で表されない場合には,丸められる. - フィールドサイズを
0
で始めると,頭の空白が0
で埋められる. - フィールドサイズの前に
+
を付けると,非負の数の前に+
符号がつく. Inf
とNaN
は,その通り印字される.
julia> @printf("%11.5e\n", -0.0078125)
-7.81250e-03
julia> @printf("%12.4e\n", -0.0078125)
-7.8125e-03
julia> @printf("%13.3e\n", -0.0078125)
-7.812e-03
julia> @printf("%13.2e\n", -0.0078125)
-7.81e-03
julia> @printf("%13.1e\n", -0.0078125)
-7.8e-03
書式文字列 %s
は,文字列形式で印字する.
s
の前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.フィールドサイズで収まらない場合には,幅を増やして印字される.
julia> @printf("%8s\n", "Hello")
Hello
julia> @printf("%8s\n", "Hello world")
Hello world
- 書式は復数使用してもよい.書式があるのに,印字すべき値がないと例外が発生する.
- 書式を指定しない文字は,そのまま印字される.
\n
は改行文字を表す.\t
はタブ(TAB)文字を表す.
julia> @printf("%.1f %15.7e\n", 0.025, -0.0078125)
0.0 -7.8125000e-03
julia> @printf("x=%.1f y=%15.7e\n", 0.025, -0.0078125) # 引数が足りない例
x=0.0 y= -7.8125000e-03
julia> @printf("%.1f %15.7e\n", 0.025)
ERROR: ArgumentError: Number of format specifiers and number of provided args differ: 2 != 1
▶ 書式を指定して,テキストファイルに書き込む.
正規乱数 10 個からなるCSVファイルを書き出そう. 関数 @printf
の第一引数に,ファイル記述子 f
を入れると,f
で示されたファイルに印字される.
using Printf
open("test1.txt", "w") do f
for i = 1:10
@printf(f, "%15.7f\n", randn())
end
end
上のプログラムが正しく動作したら,test1.txt
が作成されるはずである. このファイルを開き,数字が指定された書式で書き込まれたことを観察・確認せよ. さらに,「Excel」アプリを起動し,このファイルを1列のデータとして読み込んでみよ.
▶ CSVファイルへの書き込み
カンマ ,
で区切られた値が並ぶ行から構成されるテキストファイルを CSV(Comma Separated Value) ファイルという. 表計算ソフトウエアとのデータの輸入輸出によく用いられる. 拡張子は csv
である.
関数 @printf
を用いて,整数と浮動小数点数をカンマで区切った行を書き出そう.
なお,CSVファイルの1行目に,カンマ ,
で区切られた文字列を,各列の表題として書くことが多い.
using Printf
open("test2.csv", "w") do f
println(f, "i,x")
for i = 1:10
@printf(f, "%i,%15.7f\n", i, randn())
end
end
上のプログラムが正しく動作したら,test2.csv
が作成されるはずである. このファイルを開き,プログラムの指示通り書き込まれたか観察・確認せよ. さらに,「Excel」アプリを起動し,2列のデータとして読み込んでみよ.
▶ テキストファイルからの読み込み
▶ テキストファイルからの行単位の読み込み
関数 open( fn )
は,ファイル名 fn
のファイルをテキストとして,読み込む準備を行う. ファイル fn
が読み込めない場合には例外が発生する.
テキストファイルを読み込むためにも,関数 open
と close
を組で用いる.
以下のプログラムは,テキストファイルから,1行ずつ読み込み,それを印字する.
関数 eachline(f)
は,ファイル記述子 f
からテキストを読み込み,行に分割する.このとき,行末の改行文字は取り除かれる.
f = open("hello1.txt")
for line in eachline(f)
print(line)
end
close(f)
Hello again.
書き込みの場合と同様に,do...end
構文を用いて書くこともできる. end
が終わると関数 close(f)
が暗黙的に呼ばれる.
open("hello1.txt") do f
for line in eachline(f)
print(line)
end
end
Hello again.
▶ 文字列から数に変換する
文字列を数に変換するには,関数 parse(t, s)
を用いる. 第一引数には変換先の型を,第二引数には,変換したい文字列を書く.文字列の前後の空白は無視される.
julia> parse(Float64, " 12.3")
12.3
julia> parse(Float64, " 12.3e-2 ")
0.123
julia> parse(Int64, " 123 ") # 変換できない例
123
julia> parse(Int64, " 12.3 ")
ERROR: ArgumentError: invalid base 10 digit '.' in " 12.3 "
上のプログラムで作成したファイル test1.txt
から1行ずつ読み込み,浮動小数点数に変換して印字してみよう.
open("test1.txt") do f
for line in eachline(f)
x = parse(Float64, line)
@printf("%15.7f\n", x)
end
end
-0.3844436
0.6654147
2.0245069
-0.0008339
0.7039418
-0.7263235
0.0881307
-0.5442422
-0.2035987
-1.1870459
▶ 文字列を分割する
関数 split(s, c)
は,文字列を分割する. 第一引数の文字列 s
を,第二引数の文字列のどれか一つの文字が出現したら分割する.結果として,文字列を要素とする配列が返される.
英文テキストを,空白で区切る.
julia> split("one cup of brown suger", " ")
5-element Vector{SubString{String}}: "one" "cup" "of" "brown" "suger"
行をカンマで区切って,それぞれ整数と浮動小数点数に変換する.
julia> line = " 15 , 0.2401863"
" 15 , 0.2401863"
julia> s = split(line, ",")
2-element Vector{SubString{String}}: " 15 " " 0.2401863"
julia> parse(Int64, s[1])
15
julia> parse(Float64, s[2])
0.2401863
上のプログラムで作成した test2.csv
を読み込み,各行を整数と浮動小数点数に変換し,印字する. ただし,1行目は読み飛ばす.
using Printf
open("test2.csv") do f
i = 0
for line in eachline(f)
i += 1
i == 1 && continue
s = split(line, ",")
x = parse(Int64, s[1])
y = parse(Float64, s[2])
@printf("%5i %15.7f\n", x, y)
end
end
1 0.2658296
2 -1.2140886
3 0.3546363
4 -0.9952465
5 0.4713023
6 -0.5732707
7 -0.9260864
8 -1.0978113
9 -2.5159664
10 -1.5457950
▶ 配列に要素を加える
関数 push!(a,x)
は,配列 a
に,値 x
を付け加える.配列 a
は書き換えられる.
julia> # Int64 を要素とする配列 xs = [1, 2]
2-element Vector{Int64}: 1 2
julia> push!(xs, 3)
3-element Vector{Int64}: 1 2 3
julia> xs
3-element Vector{Int64}: 1 2 3
julia> push!(xs, 4)
4-element Vector{Int64}: 1 2 3 4
julia> xs
4-element Vector{Int64}: 1 2 3 4
要素がない配列を作るには 関数 zeros(t, 0)
を使えばよい. 第一引数 t
は,作成する配列の要素である.
以下では,Float64
型の配列(要素数 $0$)を最初に作成した.
julia> xs = zeros(Float64, 0)
Float64[]
julia> push!(xs, 1.0)
1-element Vector{Float64}: 1.0
julia> xs
1-element Vector{Float64}: 1.0
julia> push!(xs, 2.0)
2-element Vector{Float64}: 1.0 2.0
julia> xs
2-element Vector{Float64}: 1.0 2.0
▶ CSVファイルから配列を作る
以上を組合せて,CSVファイルを読み込み,値を配列としてまとめることができる.
using Printf
xs = zeros(Int64, 0)
ys = zeros(Float64, 0)
open("test2.csv") do f
i = 0
for line in eachline(f)
i += 1
i == 1 && continue
s = split(line, ",")
x = parse(Int64, s[1])
push!(xs, x)
y = parse(Float64, s[2])
push!(ys, y)
end
end
@show xs
@show ys
10-element Vector{Float64}:
0.2658296
-1.2140886
0.3546363
-0.9952465
0.4713023
-0.5732707
-0.9260864
-1.0978113
-2.5159664
-1.545795
続けて,データを描くと,
using Plots
scatter(ys, shape=:circle)
▶ CSVパッケージ
CSV
パッケージを用いると,CSVファイルを容易に扱うことができる.
CSV
パッケージを設置(インストール)していない場合は,次のように設置する.
import Pkg;
Pkg.add("CSV")
Resolving package versions...
No Changes to `~/work/memoProgClass2024w.jl/memoProgClass2024w.jl/docs/Project.toml`
No Changes to `~/work/memoProgClass2024w.jl/memoProgClass2024w.jl/docs/Manifest.toml`
CSV
パッケージに定義されている関数 CSV.read(fn, DataFrame)
を用いると,CSVファイル fn
を読み込むことができる. 結果として,拡張された配列( DataFrame
型)が返される.
# CSV
using CSV, DataFrames
df = CSV.read("test2.csv", DataFrame)
# 1番目のデータ配列
@show df[:,1]
# 2番目のデータ配列
@show df[:,2]
#
using Plots
scatter(df[:,2], shape=:circle)
データを描いた結果は,上と同じである.
★ 今回のまとめ
- 文字列の扱い
- ,出力
- 文字列の分割
- 文字列から数への変換
- 配列に要素を加える
- テキストファイル
- 書き込み
- 読み込み
- CSVファイル