Madogiwa Blog

主に技術系の学習メモに使っていきます。

Ruby:言語処理100本ノック(解説あり) 第二章:UNIXコマンドの基礎

前置き

勉強のためにrubyで言語処理100本ノックをなるべく答えを見ずにやってみる・・・!の二章目です。

言語処理100本ノック 2015

http://www.cl.ecei.tohoku.ac.jp/nlp100/

言語処理100本ノックは,実践的な課題に取り組みながら,プログラミング,データ分析,研究のスキルを楽しく習得することを目指した問題集です

Gist

rubyで言語処理100本ノック · GitHub ※作業用のGistです、アドバイス等頂けるとありがたいです(´;ω;`)

第2章: UNIXコマンドの基礎

hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ.

10. 行数のカウント

Q.行数をカウントせよ.確認にはwcコマンドを用いよ. A.countを使用してファイルの行数を出力した。

p File.open("highttemp.txt", "r").count

11. タブをスペースに置換

Q.タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ. A.bufferにファイルの中身をgsubでタブを半角スペースに置換したものを格納し、それでファイルの上書きを行った。

file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
buffer = file.read.gsub("\t"," ")
file = File.open("highttemp.txt", "w", encoding: 'Shift_JIS')
file.write(buffer)
file.close

12. 1列目をcol1.txtに,2列目をcol2.txtに保存

Q.各行の1列目だけを抜き出したものをcol1.txtに,2列目だけを抜き出したものをcol2.txtとしてファイルに保存せよ.確認にはcutコマンドを用いよ. A.ファイルの内容をsplitで配列[高知県,江川崎,41,2013-08-12]に変換し、1列目だけ取得したものをcol1、2列目だけ取得したものをcol2に保存し、それぞれの内容でファイルを作成、上書きするようにした。

file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
buffer = []
file.each{ |row| buffer << row.encode(Encoding::UTF_8).split(" ") }
col1,col2 = buffer.map{ |r| r[0] }, buffer.map{ |r| r[1] }
File.open("./col1.txt", "w"){ |file| file.puts col1 }
File.open("./col2.txt", "w"){ |file| file.puts col2 }

13. col1.txtとcol2.txtをマージ

Q.12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ. A.col1.txt,col2.txtを読み込んだ結果を改行区切りで配列にし、buffer[ col1[i], col2[i] ]の形式で格納。その後、bufferの各要素をjoin("\t")でタブで結合し、ファイルに書き込みを行った。

col1 = File.open("./col1.txt", "r").read.split("\n")
col2 = File.open("./col2.txt", "r").read.split("\n")
buffer = []
col1.length.times{ |i| buffer << [ col1[i], col2[i] ] }
buffer = buffer.map{|b| p b.join("\t")}
File.open("./col1_col2.txt", "w"){ |file| file.puts buffer }

14. 先頭からN行を出力

Q.自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ. A.STDIN.gets.to_iで標準入力をIntgerに変換し取得。その後、入力値の数だけファイルの行をfile.gets.encode(Encoding::UTF_8)で出力した。

cnt = STDIN.gets.to_i
file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
cnt.times{ |i| p file.gets.encode(Encoding::UTF_8) }

15. 末尾のN行を出力

Q.自然数Nをコマンドライン引数などの手段で受け取り,入力のうち末尾のN行だけを表示せよ.確認にはtailコマンドを用いよ. A.STDIN.gets.to_iで標準入力をIntgerに変換し取得。その後、ファイルの内容をbufferに格納し、入力値の数だけ末尾から出力buffer.popした。

cnt = STDIN.gets.to_i
buffer = []
file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
file.each{ |row| buffer << row.encode(Encoding::UTF_8) }
cnt.times{ |i| p buffer.pop }

16. ファイルをN分割する

Q.自然数Nをコマンドライン引数などの手段で受け取り,入力のファイルを行単位でN分割せよ.同様の処理をsplitコマンドで実現せよ. A.STDIN.gets.to_iで標準入力をIntgerに変換し取得。ファイルの内容をbufferに格納し、Array.shiftを使いたかったので、現在の要素数file_lengthに保存。その後、入力値の回数分ファイルhighttemp_#{i}.txtを作成し、各ファイルにファイルの行数 / 入力値分だけbuffer.shiftで書き込むようにした。また最後のファイルには、余りの行を全て書き込む仕様としました。

cnt = STDIN.gets.to_i
buffer = []
file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
file.each{ |row| buffer << row.encode(Encoding::UTF_8) }
file_length = buffer.length
cnt.times do |i|
File.open("highttemp_#{i}.txt","w") do |file|
(file_length / cnt).times{ file.puts buffer.shift }
buffer.each{ |b| file.puts b } if i + 1 == cnt
end
end

17. 1列目の文字列の異なり

Q.1列目の文字列の種類(異なる文字列の集合)を求めよ.確認にはsort, uniqコマンドを用いよ. A.buffer.map{ |row| row.split(" ")[0] }でファイルの1列目だけの配列を作成し、uniqで要素を一意にした。

buffer = []
file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
file.each{ |row| buffer << row.encode(Encoding::UTF_8) }
p buffer.map{ |row| row.split(" ")[0] }.uniq

18. 各行を3コラム目の数値の降順にソート

Q.各行を3コラム目の数値の逆順で整列せよ(注意: 各行の内容は変更せずに並び替えよ).確認にはsortコマンドを用いよ(この問題はコマンドで実行した時の結果と合わなくてもよい). A.buffer.map{ |row| row.split(" ") }でファイルの内容を配列に格納し、sort{ |a, b| a[2] <=> b[2] }.reverseで3列目(気温)の降順で並べ替えた。

buffer = []
file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
file.each{ |row| buffer << row.encode(Encoding::UTF_8) }
p buffer.map{ |row| row.split(" ") }.sort{ |a, b| a[2] <=> b[2] }.reverse

19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる

Q.各行の1列目の文字列の出現頻度を求め,その高い順に並べて表示せよ.確認にはcut, uniq, sortコマンドを用いよ. A.buffer.map{ |row| row.split(" ")[0] }で1列名(県名)のみの配列を作成し、 buffer.map{|row| [row,buffer.count(row)] }で県名と出現数を持つを作成、その後uniqで一意とし、sort{ |a, b| a[1] <=> b[1] }.reverseで出現数の降順に並び替えた。

buffer = []
file = File.open("highttemp.txt", "r", encoding: "Shift_JIS")
file.each{ |row| buffer << row.encode(Encoding::UTF_8) }
buffer = buffer.map{ |row| row.split(" ")[0] }
p buffer.map{|row| [row,buffer.count(row)] }.uniq.sort{ |a, b| a[1] <=> b[1] }.reverse