#+File Created: <2016-07-16 Sat 13:44>
#+Last Updated: <2018-11-21 Wed 20:51>

tab 区切りファイル読込み/書込みの各言語でのやりかたの違いを整理する.


参考:
配列操作の比較表: Ruby, Python, JavaScript, Perl, C++ - bkブログ
http://0xcc.net/blog/archives/000043.html

1 はじめに

私は普段の生活においては perl でプログラムを書いている.
ささっと書いて捨てちゃうことが多いが,
そのようなプログラムで最も多いぱたーんは以下のようなものである:

  1. 何か(tab 区切りとかの)ファイルを読込む.
  2. 何かやる(読み込んだデータを加工したり計算したりする).
  3. 別のファイルに結果等を書き出す.

Python を始めとした色んな言語をこれから学んでいくにあたって,
上記操作のやり方の違いををまずは見ていくのが自分にとっては一番しっくり来るかなぁと思ったのでまとめておく.

2 Perl

これが基準

 1: use strict;
 2: use warnings;
 3: 
 4: my $ifile='input.txt';
 5: my $ofile='output.perl.txt';
 6: 
 7: # input
 8: open my $fh,"<",$ifile;
 9: my $cnt=0;
10: my @head;
11: my $data;
12: while(<$fh>) {
13:   chomp;
14:   my @bf=split(/\t/,$_);
15:   # 様々な状況に対応できるように
16:   unless($cnt) { # header
17:     @head=@bf;
18:   }else {        # data
19:     push(@{$data},\@bf);
20:   }
21:   $cnt++;
22: }
23: close $fh;
24: 
25: # output
26: my $prnt;
27: $prnt=join("\t",@head)."\n";
28: for my $d (@{$data}) {
29:   $prnt.=join("\t",@{$d})."\n";
30: }
31: open my $ofh,">",$ofile;
32: print $ofh $prnt;
33: close $ofh;
34: 
35: print $prnt; # 標準出力にも書き出す場合
name  age origin  gender  idata fdata type
foo 10  jpn M 50  0.25  X
bar 30  usa F 25  0.38  P
hoge  10  jpn F 4 1 P
fuga  40  eng M     X
fuga  35    F 9 0.1 X

header の名前を key にして hash の配列にしてもよい.

3 Python

python での注意点と参考 URL:

 1: import sys
 2: 
 3: ifile='input.txt'
 4: ofile='output.python.txt'
 5: 
 6: fh=open(ifile,'r')
 7: # lines=fh.readlines() # 全部読み込んで lines に入れる場合.
 8: 
 9: # input
10: # perl と同じ感じでかいてみる.
11: cnt=0
12: head=[]
13: data=[]
14: for li in fh:   # li に 1 行毎(改行含む)が入ってくる
15:   bf = li[:-1].split("\t")  # 後ろの改行を取り除いて(li[:-1]) split
16:   if not cnt:
17:      head=bf
18:   else:
19:      data.append(bf)  # push ではなく append
20:   cnt+=1
21: fh.close
22: 
23: # 文字列として入ってることを確認する.
24: #print(head)
25: #print(data)
26: 
27: # hash の配列として入れてみる
28: # hashs[0]['name'] こんな感じで各々の要素にアクセスする.
29: hashs=[]
30: for dr in data:
31:   hash={}
32:   # print(dr)
33:   # sys.exit(0) # ここで終わる時(for debug)
34:   for j,d in enumerate(dr):
35:      # j が 1 か 4 のときは, 整数として入れる
36:      # j が 5      のときは, 浮動小数点として入れる. 面倒...
37:      dk = d
38:      if not d=='':
39:        if j==1 or j==4:
40:          dk = int(d)
41:        elif j==5:         # elsif ではなく elif
42:          dk = float(d)
43:      hash[head[j]]=dk
44:   hashs.append(hash)
45: 
46: #print(hashs)
47: #print(hashs[0]['name'])
48: 
49: # output
50: ofh=open(ofile,'w')
51: prnt="\t".join(head)+"\n"
52: for d in data:
53:   prnt+="\t".join(d)+"\n"
54: print(prnt)
55: ofh.write(prnt)
56: ofh.close()
name  age origin  gender  idata fdata type
foo 10  jpn M 50  0.25  X
bar 30  usa F 25  0.38  P
hoge  10  jpn F 4 1 P
fuga  40  eng M     X
fuga  35    F 9 0.1 X

dataframe とか使う場合はまた別でまとめようと思うが, 一応書いておく.

import pandas as pd
ifile='input.txt'
ofile='output.pandas.txt'

df = pd.read_csv(ifile,sep="\t",header=0)

# 何かやる
# 0 行目の 'name' 列を書き換え
df.ix[0,'name'] = 'foo2'
df.to_csv(ofile,sep="\t",index=False)

print(df)
   name  age origin gender  idata  fdata type
0  foo2   10    jpn      M   50.0   0.25    X
1   bar   30    usa      F   25.0   0.38    P
2  hoge   10    jpn      F    4.0   1.00    P
3  fuga   40    eng      M    NaN    NaN    X
4  fuga   35    NaN      F    9.0   0.10    X

4 Julia

Julia での注意点と参考 URL:

全般的に, 型を意識する必要がある(? 全部 Any でいいのかなぁ)のでちょっと面倒くさい印象.
文字列は '' ではなく "" で囲わないといけない(Java と同じ)
and, or は && || (これも Java と同じ)
if 文は, if .. elseif .. end という感じで書く.
関数は function hoge(x) … end という感じで書く.
配列の添字は 1 から始まる.
二次元配列は arr[1,2] (1行2列目), 配列の配列は arr
hash の宣言: Dict{keyの型, value の型}()
主な型指定: Int64, Float64, ASCIIString, Any(何を入れてもいい型)
文字列連結は "." でも "+" でもなく, 何と "*" を使うらしい. 何で?

 1: ifile="input.txt"
 2: ofile="output.julia.txt"
 3: 
 4: #fh=open(ifile,"r")
 5: #str=readall(fh)  # 一気に読み込む場合はこうする.
 6: #print(str)
 7: 
 8: head=[]  #head=Array{Any,1}()
 9: data=Array{Array{Any,1},1}()  # 1x1 の, 配列の配列を作っておく.
10: # data=[][] これじゃダメらしい.
11: 
12: # filehandle は以下のように書くのが普通のようだ
13: open(ifile,"r") do fh
14:     cnt=0
15:     for li in eachline(fh)  # 1 行ずつ読んでいく
16:         #print(li)
17:         li = chomp(li)          # 改行を取り除く
18:         # li = rstrip(li,'\n')  # 改行削除はこれでもいい
19:         bf = split(li,"\t")     # \t で split
20:         # println(bf)
21:         if cnt == 0             # 1 行目(header)
22:             append!(head,bf)       # head=bf ではエラーとなる. append! を使う
23:             #for i in 1:length(bf)
24:             #   push!(head,bf[i])
25:             #end
26:         else                    # 2 行目以降(でーた)
27:             push!(data,bf)
28:         end
29:         cnt+=1
30:     end
31: end
32: # close(fh) # 自動で閉じるので書かなくてもいいっぽい.
33: 
34: # data[][] を hash の配列にしてみる.
35: hashs=[]
36: for dt in data
37:     hash = Dict{Any,Any}()
38:      for i in 1:length(dt)
39:         hash[head[i]]=dt[i]
40:      end
41:     push!(hashs,hash)
42: end
43: 
44: println(head)
45: #println(data)
46: #println(data[1][1])
47: 
48: println(head[1])
49: println(hashs[1]["fdata"])
50: println(hashs[2]["fdata"])
51: # exit() プログラムを終わらせるとき
52: 
53: # 型キャスト的なことは, parse(type,str) を用いる
54: itx = parse(Float64,hashs[1]["fdata"]) + parse(Float64,hashs[2]["fdata"])
55: println(itx)
56: 
57: # ファイル書き込み
58: open(ofile,"w") do fp
59:    write(fp,join(head,"\t")*"\n")
60:    for d in data
61:       write(fp,join(d,"\t")*"\n")
62:    end
63: end
Any["name","age","origin","gender","idata","fdata","type"]
name
0.25
0.38
0.63

5 Ruby

Ruby での注意点と参考 URL:

 1: ifile='input.txt'
 2: ofile='output.ruby.txt'
 3: 
 4: fh=open(ifile,"r")
 5: # str=fh.read     # 一気に全部読み込む場合
 6: cnt=0
 7: head=[]    # head=Array.new でも良い
 8: data=[]
 9: while li = fh.gets
10:   #print li
11:   li=li.chomp         # 改行取り除き
12:   bf=li.split("\t")   # tab で split
13:   #print(bf)
14:   #exit
15:   if cnt==0
16:     head=bf
17:   else
18:     data.push(bf)
19:   end
20:   cnt+=1
21: end
22: fh.close
23: 
24: print(head)
25: print("\n")
26: #print(data[0])
27: #exit   ここで終わるとき (for debug)
28: 
29: # hash の配列にしてみる.
30: hashs=[]
31: for dr in data
32:   hash={}
33:   dr.each_with_index do |d,j|   # enumerate
34:     dk=d
35:     if not d==''
36:        if j==1 or j==4
37:          dk=d.to_i         # 整数型へキャスト
38:        elsif j==5
39:          dk=d.to_f         # 浮動小数点型へキャスト
40:        end
41:     end
42:     hash[head[j]]=dk
43:   end
44:   hashs.push(hash)
45: end
46: 
47: print(hashs[0])
48: print("\n")
49: 
50: itx = hashs[0]["fdata"] + hashs[1]["fdata"]
51: puts(itx)
52: 
53: # file 書き出し
54: ofh=open(ofile,"w")
55: ofh.write(head.join("\t")+"\n")
56: for dt in data
57:    str=dt.join("\t")
58:    ofh.write(str+"\n")
59: end
60: ofh.close
["name", "age", "origin", "gender", "idata", "fdata", "type"]
{"name"=>"foo", "age"=>10, "origin"=>"jpn", "gender"=>"M", "idata"=>50, "fdata"=>0.25, "type"=>"X"}
0.63

6 R

おまけ(1)
read.table でファイルを読むプログラムを一応作っておく.

R での注意点と参考 URL:

 1: ifile <- 'input.txt'
 2: ofile <- 'output.R.txt'
 3: 
 4: data <- read.table(ifile,sep="\t",head=T)
 5: data
 6: names(data)    # header の名前
 7: data['name']   # 列の取得
 8: 
 9: # quote=F  : "" を付けない
10: # append=F : 上書き
11: write.table(data,ofile,quote=F,sep="\t",col.names=T,row.names=F,append=F,na="")
  name age origin gender idata fdata type
1  foo  10    jpn      M    50  0.25    X
2  bar  30    usa      F    25  0.38    P
3 hoge  10    jpn      F     4  1.00    P
4 fuga  40    eng      M    NA    NA    X
5 fuga  35             F     9  0.10    X
[1] "name"   "age"    "origin" "gender" "idata"  "fdata"  "type"  
  name
1  foo
2  bar
3 hoge
4 fuga
5 fuga

7 Java

おまけ(2)

Java での注意点と参考 URL:

 1: import java.io.File;
 2: import java.io.FileReader;
 3: import java.io.BufferedReader;
 4: import java.util.HashMap;
 5: import java.util.Map;
 6: import java.util.List;
 7: import java.util.ArrayList;
 8: import java.io.FileWriter;
 9: import java.io.BufferedWriter;
10: import java.io.PrintWriter;
11: public class fread {
12:   public static void main(String[] args) {
13:     String ifile="input.txt";
14:     String ofile="output.java.txt";
15: 
16:     List<Map> data = new ArrayList(); 
17:     List<String> head = new ArrayList();
18:     try {
19:       File file=new File(ifile);
20:       BufferedReader br = new BufferedReader(new FileReader(file));
21:       int cnt=0;
22:       String str;
23:       while((str=br.readLine())!=null) {
24:         String [] row=str.split("\t");
25:         if(cnt==0) {
26:           for(String s : row) { // 何か気持ち悪いけど
27:              head.add(s);
28:           }
29:         }else {
30:           Map<String,String> hash = new HashMap();
31:           int i=0;
32:           for(String s : head) {
33:              hash.put(s,row[i++]);
34:           }
35:           data.add(hash);
36:         }
37:         cnt++;
38:       }
39:     }catch(Exception e) {
40:       System.out.println(e);
41:     }
42:     System.out.println(String.join("\t",head));
43:     System.out.println(data.get(0).get("name"));
44:     // System.exit(0);  // 終わるとき
45: 
46:     // ファイル書き出し
47:     try {
48:       File ofh          = new File(ofile);
49:       FileWriter fw     = new FileWriter(ofh);
50:       BufferedWriter bw = new BufferedWriter(fw);
51:       PrintWriter pw    = new PrintWriter(bw);
52:       String prnt=String.join("\t",head)+"\n";
53:       for(Map mp : data) {       // 拡張 for
54:         for(String s : head) {   // 苦肉の策
55:           prnt+=mp.get(s)+"\t";
56:         }
57:         prnt.trim();  // 先頭, 最後の空白を取り除く
58:         prnt+="\n";
59:       }
60:       System.out.print(prnt);
61:       pw.print(prnt);
62:       pw.close();
63:     }catch(Exception e) {
64:       System.out.println(e);
65:     }
66:   }
67: }
name  age origin  gender  idata fdata type
foo
name  age origin  gender  idata fdata type
foo 10  jpn M 50  0.25  X 
bar 30  usa F 25  0.38  P 
hoge  10  jpn F 4 1 P 
fuga  40  eng M     X 
fuga  35    F 9 0.1 X

8 かんそう

perl, python, ruby は似たような感じ.
ruby は書きやすかった. きっちりしてる感じ.
ruby に比べると python はやっぱり何か, 開きっぱなしで閉じてない感じがいまんとこしっくりこないんだけどなぁ.
常に何か忘れてるような, そんな一抹の不安を覚えたりします. 慣れればなんともないんでしょーか.
変数のスコープもよくわかんないし…
Julia も上記の言語と同じような感じでいけるのかなぁと思っていたのですが, 何かいちいち勝手が違う感じ.
こんな感じでうまくいくかな, という適当さが全然通用しなかった. python, ruby は適当にやっても何とかなったんだけど…
色々と調べることが多かった.
つーか文字列の連結が "*" って意味わかんないような… こういうの検索とかで探しにくいから困るんだよなぁ.
"." か "+" か, 最悪 "&" とか, わかりやすい感じにして欲しいなぁ… あるいは concat とか.
R, Java はおまけでやってみましたが, いまいちうまく比較できなかったのでやらなくてもよかったかも.

Comments