言語処理100本ノック【第2章 前編】
課題研究で言語処理100本ノック 2015に取り組んでいます。前回の続きです。使用言語はPython3です。今回からは第2章 UNIXコマンドの基礎 です。まずは第2章前半の5つを説明します。
第2章の概要
hightemp.txtは,日本の最高気温の記録を「都道府県」「地点」「℃」「日」のタブ区切り形式で格納したファイルである.以下の処理を行うプログラムを作成し,hightemp.txtを入力ファイルとして実行せよ.さらに,同様の処理をUNIXコマンドでも実行し,プログラムの実行結果を確認せよ
10. 行数のカウント
行数をカウントせよ.確認にはwcコマンドを用いよ.
f = open("hightemp.txt", "r") print(len(f.readlines())) f.close()
open()
はファイルオブジェクトを取得する関数です。第1引数には対象のファイル名、第2引数にはオブジェクトを取得する際のモードを指定します。このプログラムではファイルを読み込ん(read)で操作するので、 r モードを指定しました。実はopen()
関数の第2引数はデフォルトで r を指定するので、ファイルを読み込みたい時は、モードを省略することもできます。
readlines()
はopen()
関数のメソッドで、読み込んだファイルをリストにして返します。リストの要素はファイルの各行です。例えば、
みかんは美味しい りんごも美味しい いちごも美味しい
というテキストファイルに対して、readlines()
メソッドを用いると ["みかんは美味しい", "りんごも美味しい", "いちごも美味しい"] というリストが返ってきます。このリストの長さを数えることで行数をカウントしました。
取得した(開いた)ファイルオブジェクトは、close()
メソッドで閉じ、メモリを解放します。(C言語と一緒)
UNIXコマンド
wc -l hightemp.txt
wc
はファイルのバイト数、行数、文字数または単語数をカウントするコマンドです。-l
をオプションに指定すると、ファイルの行数を数えることが出来ます。
実行結果
24
11. タブをスペースに置換
タブ1文字につきスペース1文字に置換せよ.確認にはsedコマンド,trコマンド,もしくはexpandコマンドを用いよ.
f = open("hightemp.txt", "r") print(f.read().replace("\t", " "), end="") f.close()
read()
はopen()
関数のメソッドで、読み込んだファイルの全てのデータを文字列型で読み込みます。replace()
は文字列を置換する関数で、第1引数に指定した文字を第2引数に指定した文字に置き換えます。
read()
メソッドで読み込んだデータをそのまま出力すると、print()
関数の都合上末尾に 改行コードが付与されてしまいます。ここでは、end
オプションを用いて末尾で改行されないようにしました。end
オプションは指定した文字列を出力の末尾に付与します。(デフォルトは \n)
UNIXコマンド
expand -t 1 hightemp.txt
私はUNIXコマンドにはexpand
を使いました。expand
はタブをスペースに変換するコマンドです。-t
オプションでタブ幅( = スペースに変換すると何文字分の幅を取っているか)を指定しています。
実行結果
高知県 江川崎 41 2013-08-12 埼玉県 熊谷 40.9 2007-08-16 岐阜県 多治見 40.9 2007-08-16 (中略) 山形県 鶴岡 39.9 1978-08-03 愛知県 名古屋 39.9 1942-08-02
12. 1列目をcol1.txtに,2列目をcol2.txtに保存
f = open("hightemp.txt", "r") a = open("col1.txt", "w") b = open("col2.txt", "w") lines = f.readlines() for line in lines: datas = line.split() a.writelines(datas[0] + "\n") b.writelines(datas[1] + "\n") f.close() a.close() b.close()
結果を出力するファイルである col1.txt と col2.txt は、w モードで取得します。wモードを用いると、取得したファイルオブジェクトに対して書き込み(write)を行えます。ファイルの行ごとのデータを持つ lines リストに対してfor文を実行し、得た文字列 line に対して列のデータを取得しました。列は縦向きに並んでいるデータです。
writelines()
はopen()
関数のメソッドで、ファイルオブジェクトに対して1行ずつ書き込みを行います。print()
関数と違ってデフォルトで末尾に改行コードが付与されないので、改行する場合は別に \n を追記する必要があります。
UNIXコマンド
cut -f 1 hightemp.txt > col1.txt #1列目について cut -f 2 hightemp.txt > col2.txt #2列目について
cut
は文字列から所定の位置にある文字列を抜き出すコマンドです。-f
オプションを用いると、指定した順番のフィールドだけを切り出す事ができます。フィールドとは特定の区切り文字列で区切られた文字列のことです。例えば、
りんご みかん いちご
という文字列に対して、"りんご"、”みかん"、”いちご” といったフィールドが順番に得られます。cut
コマンドで得た出力を >
を用いて別のファイルに保存しました。
実行結果
col1.txt
高知県 埼玉県 岐阜県 (中略) 山形県 愛知県
col2.txt
江川崎 熊谷 多治見 (中略) 鶴岡 名古屋
13. col1.txtとcol2.txtをマージ
12で作ったcol1.txtとcol2.txtを結合し,元のファイルの1列目と2列目をタブ区切りで並べたテキストファイルを作成せよ.確認にはpasteコマンドを用いよ.
a = open("col1.txt", "r") b = open("col2.txt", "r") f = open("col.txt", "w") col1 = a.readlines() col2 = b.readlines() for data1, data2 in zip(col1, col2): f.writelines(data1.replace("\n", "") + "\t" + data2); a.close() b.close() f.close()
col1.txt と col2.txt の結果をそれぞれ行ごとのリストで取得します。zip()
は、複数のシーケンス型オブジェクトをまとめてループさせる為の関数です。このプログラムでは、2つのリストに対して先頭から同じ順番に要素を得ています。col1 から得られるデータには、不要な \n がついているので、replace()
を使って除去しました。
UNIXコマンド
paste col1.txt col2.txt > col.txt
paste
は複数のファイルを行単位で連結するコマンドです。-d
オプションを指定しない場合、タブ文字で連結されます。
実行結果
高知県 江川崎 埼玉県 熊谷 岐阜県 多治見 (中略) 山形県 鶴岡 愛知県 名古屋
14. 先頭からN行を出力
自然数Nをコマンドライン引数などの手段で受け取り,入力のうち先頭のN行だけを表示せよ.確認にはheadコマンドを用いよ.
import sys args = sys.argv f = open("hightemp.txt", "r") lines = f.readlines() for i in range(int(args[1])): print(lines[i], end = "") f.close()
コマンドライン引数をプログラムで使用するために、sys
モジュールをインポートしました。sys.argv
はコマンドライン引数をリストで返す関数で、args[0]
では実行したプログラム名が得られます。例えば、
python 13.py 3
といった風にプログラムを実行すると、args[0] = "13.py"、args[1] = "3" が取得できます。このプログラムでは args[1]
で自然数N(ただし文字列型)が得られることが分かっているので、int()
関数で int型に変換して、N個分の行を出力しました。
UNIXコマンド
head -n N hightemp.txt
head
はテキストファイルの先頭のN行またはNバイトを抜き出すコマンドです。-n
オプションを指定すると、指定したテキストファイルの先頭からN行を抜き出します。
実行結果
N = 3 の時
高知県 江川崎 41 2013-08-12 埼玉県 熊谷 40.9 2007-08-16 岐阜県 多治見 40.9 2007-08-16
実はこの記事は1度途中まで書いていたのですが、途中でWifiが切れてデータが吹っ飛んでしまったので、泣く泣く最初から書き直しました。こまめに保存しとくのってめっちゃ大事や…