wcコマンドの続き Arrayだと思ってたらStringだった
引き続き沼
検証パターンとその課題を綴る
検証①Stringで取得してファイル指定の時はそれを分割して配列化 => 処理
- 対象データの格納変数として
input_contents
準備 ARGF.read : $stdin.read
このどっちかが入る- 標準入力の場合とファイル指定時の合計値は一つの処理で完結する
- ファイル指定の時の個々の
ファイル内容の処理結果・ファイル名出力はどうするか? <= ここが課題
input_contents = ARGF.read #ファイル指定時
これになった時、初回で.read
してしまってるので4.の処理をするには不向き。
ファイル指定時のみinput_content
に対して、
input_contents2
など異なる変数にARGF.argv
を使う => 格納されないinput_contents
を二次元配列にする => 格納されない
同一コード内でARGF
が使えるのは1回きり?
文字列で丸ごと扱うと、
- ファイル複数指定時の処理
- 標準入力時の処理全て
これを一気にできるので良いと思ったけど、配列として初めから取得した方がよさそう。
検証②ファイル指定を配列、標準入力は文字列として処理
input_contents = ARGF.argv #ファイル指定時
- 対象データの格納変数として
input_contents
準備 ARGF.argv : $stdin.read
このどっちかが入る- ファイル指定 => Array、標準入力 => String
if input_contents = Array
=> ファイル指定の時の個々の処理each
分の中で配列値をread
あるいはFile.read(配列変数)
するread
したものに対して行数、文字数、バイト数- ファイル名出力
- ★
標準入力の時の処理
String
として処理する =>この場合★(ファイル指定時はArray
なので同一処理が不可)- そのため、★同一処理を進めるならファイル指定(
Array
)=>ファイル指定(String
)にする処理が必要?
6.★ => ファイル指定の際の合計値
- 5.で一緒にできたら良いけれど、Array
=> String
問題がある 変換するプログラムがいる
- あるいは4.の処理each
内部で集計して抜けたら合計値を出力するようにするか
設計案
上記のどれを取ればリファクタリングした、冗長的でないコードになるのか 全部試す前に一つずつ設計してみる。これ以外の設計の仕方が思いつかない。
- 処理:A => ファイル指定、 B => 標準入力
仮設計①:ABどちらも文字列(String
)として使う場合
仮設計①
input_contents = (!ARGF.argv.count.zero? ? ARGF.read : $stdin.read) #コマンドライン引数で指定した時に「ファイル名」ごとに中身を配列として分けるにはどうすればよいか? #コマンドライン引数で指定した時に「ファイル名」をどう取得するか? # 合計値の出力(ファイル複数指定、標準入力)
コマンドライン引数入力時に先に読み取ってしまうと、各ファイルごとの集計ができなそうなので却下。
仮設計②:AはArray,BはStringとして使う場合
仮設計②
input_contents = (!ARGF.argv.count.zero? ? ARGF.argv : $stdin.read)
この場合はFile.read(変数)
を使う必要がある。コマンドライン引数でinput_contents
に格納されたファイル名をeach文
で読み込み、集計していく。
Aに対して、
- ファイル名の配列を
each
で取り出し File.read(変数)
で読み込み- 改行数、ワード数、バイト数を集計
- ※複数の場合はここで合計値用の配列変数に集計していく※
- 集計後は上記とファイル名を出力
Aの複数の場合
- 上記の繰り返し出力が終わったあと、
- 合計値用の配列変数を出力
Bに対して、文字列として取得してるので以下で処理できる
標準入力を文字列で取得しときの処理
print " #{content.count("\n")}" unless options['l'] print " #{content.split(/\s+/).size}" print " #{content.bytesize}" end
仮設計③:ABどちらも配列(Array
)として使う場合
仮設計③
input_contents = (!ARGF.argv.count.zero? ? ARGF.argv : readlines)
Aに対して、
- ファイル名の配列を
each
で取り出し File.read(変数)
で読み込み- 改行数、ワード数、バイト数を集計
- ※複数の場合:合計値を変数に格納していくdata_sum =[改行数,ワード数、バイト数]※
- 集計後は上記とファイル名を出力
Aの複数の場合
- 上記の繰り返し出力が終わったあと、
- 合計値用の配列変数を出力
Bに対して、
- ["ファイル名\n","ファイル名\n"...]の状態なので繰り返し処理の内部で改行を省く
- 同時にファイル読み込みをしないで
content = ファイル名
として - 改行数、ワード数、バイト数を集計
- 合計値を変数に格納していくdata_sum =[改行数,ワード数、バイト数]
- 合計値用の配列変数を出力
共通部分はメソッドで切り出して都度呼び出すようにするべきか?
使えそうなメソッドなど
ファイル読み込み
変数.read
=> 文字列が対象File.read(変数)
=> 配列が対象
コマンドライン引数か標準入力
ARGF.file
=> ファイル指定で1ファイルのみ格納 文字列、 標準入力で#<IO:<STDIN>>
が返ってくるARGF.argv
=> ファイル指定で配列で取得、標準入力の場合 空ARGV
=> ファイル指定で配列で取得、標準入力の場合 空readlines
=> ファイル指定で指定ファイルの内容を改行ごとに配列格納。複数だと全内容を1要素として格納、標準入力での出力 = 改行含めて取得 "ファイル名\n…複数" この文字を配列として改行ごとに格納readline
=> 1行のみなのであまり使えないSTDIN.gets
=> ファイル指定では動作が止まる 、標準入力での出力 = 先頭一つのみ取得"ファイル名\n" 文字列$stdin.read
=> ファイル指定では動作が止まる 、標準入力での出力 = 改行含めて取得 "ファイル名\n…複数" 文字列$stdin.readlines
=> ファイル指定では動作が止まる 、標準入力での出力 = 改行含めて取得 ["ファイル名\n"...複数] 配列として取得
その他
9.File.read(文字列)
=> 内容を読み込み
メソッド引用
.filename -> String
:現在開いている処理対象のファイル名を返します。 標準入力に対しては - を返します。組み込み変数 $FILENAME と同じです。
file -> IO[permalink][rdoc][edit] 現在開いている処理対象の File オブジェクト(または IO オブジェクト)を返します。
$ echo "foo" > foo $ echo "bar" > bar $ ruby argf.rb foo bar ARGF.file # => #<File:foo> ARGF.read(5) # => "foo\nb" ARGF.file # => #<File:bar> ARGFが現在開いている処理対象が標準入力の場合、$stdin を返します。
参考リンク
逆引きRubyあった・・・