第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 Printfjulia> @printf("%5i\n", -41)-41julia> @printf("%05i\n", 413)00413julia> @printf("%+5i\n", 413)+413julia> @printf("%+5i\n", -413)-413julia> @printf("%+05i\n", -413)-0413julia> @printf("%+5i\n", -41131)-41131
書式文字列 %f は,小数を印字する.
- fの前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.指定されたフィールドサイズで収まらない場合には,幅を増やして印字される.
- フォールドサイズに続けて,ピリオド .と整数を付けると,小数点以下の桁数を指定する.指定された小数点以下桁数で表されない場合には,丸められる.
- フィールドサイズを 0で始めると,頭の空白が0で埋められる.
- フィールドサイズの前に +を付けると,非負の数の前に+符号がつく.
- Infと- NaNは,その通り印字される.
julia> @printf("%8.3f\n", -2e-2)-0.020julia> @printf("%08.0f\n", -0.252)-0000000julia> @printf("%8.1f\n", -0.252)-0.3julia> @printf("%08.2f\n", -0.252)-0000.25julia> @printf("%8.3f\n", -0.252)-0.252
書式文字列 %e は,指数形式で印字する.
- eの前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.フィールドサイズで収まらない場合には,幅を増やして印字される.
- フォールドサイズに続けて,ピリオド .と整数を付けると,小数点以下の桁数を指定する.指定された小数点以下桁数で表されない場合には,丸められる.
- フィールドサイズを 0で始めると,頭の空白が0で埋められる.
- フィールドサイズの前に +を付けると,非負の数の前に+符号がつく.
- Infと- NaNは,その通り印字される.
julia> @printf("%11.5e\n", -0.0078125)-7.81250e-03julia> @printf("%12.4e\n", -0.0078125)-7.8125e-03julia> @printf("%13.3e\n", -0.0078125)-7.812e-03julia> @printf("%13.2e\n", -0.0078125)-7.81e-03julia> @printf("%13.1e\n", -0.0078125)-7.8e-03
書式文字列 %s は,文字列形式で印字する.
- sの前に整数(フィールドサイズ)を付けると,その整数の幅で右寄せされて印字される.フィールドサイズで収まらない場合には,幅を増やして印字される.
julia> @printf("%8s\n", "Hello")Hellojulia> @printf("%8s\n", "Hello world")Hello world
- 書式は復数使用してもよい.書式があるのに,印字すべき値がないと例外が発生する.
- 書式を指定しない文字は,そのまま印字される.
- \nは改行文字を表す.
- \tはタブ(TAB)文字を表す.
julia> @printf("%.1f %15.7e\n", 0.025, -0.0078125)0.0 -7.8125000e-03julia> @printf("x=%.1f y=%15.7e\n", 0.025, -0.0078125) # 引数が足りない例x=0.0 y= -7.8125000e-03julia> @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
endHello again.▶ 文字列から数に変換する
文字列を数に変換するには,関数 parse(t, s)を用いる. 第一引数には変換先の型を,第二引数には,変換したい文字列を書く.文字列の前後の空白は無視される.
julia> parse(Float64, " 12.3")12.3julia> parse(Float64, " 12.3e-2 ")0.123julia> parse(Int64, " 123 ") # 変換できない例123julia> 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])15julia> 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 2julia> push!(xs, 3)3-element Vector{Int64}: 1 2 3julia> xs3-element Vector{Int64}: 1 2 3julia> push!(xs, 4)4-element Vector{Int64}: 1 2 3 4julia> xs4-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.0julia> xs1-element Vector{Float64}: 1.0julia> push!(xs, 2.0)2-element Vector{Float64}: 1.0 2.0julia> xs2-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 ys10-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ファイル