ヌンタコのプログラミング学習ブログ

フィヨルドブートキャンプ37期生

wcコマンドの続き Arrayだと思ってたらStringだった

引き続き沼

検証パターンとその課題を綴る

検証①Stringで取得してファイル指定の時はそれを分割して配列化 => 処理

  1. 対象データの格納変数としてinput_contents準備
  2. ARGF.read : $stdin.readこのどっちかが入る
  3. 標準入力の場合とファイル指定時の合計値は一つの処理で完結する
  4. ファイル指定の時の個々の

ファイル内容の処理結果・ファイル名出力はどうするか?  <= ここが課題

input_contents = ARGF.read #ファイル指定時

これになった時、初回で.readしてしまってるので4.の処理をするには不向き。 ファイル指定時のみinput_contentに対して、

  1. input_contents2など異なる変数にARGF.argvを使う => 格納されない
  2. input_contentsを二次元配列にする => 格納されない

同一コード内でARGFが使えるのは1回きり?

文字列で丸ごと扱うと、

  • ファイル複数指定時の処理
  • 標準入力時の処理全て

これを一気にできるので良いと思ったけど、配列として初めから取得した方がよさそう。

検証②ファイル指定を配列、標準入力は文字列として処理

input_contents = ARGF.argv #ファイル指定時
  1. 対象データの格納変数としてinput_contents準備
  2. ARGF.argv : $stdin.readこのどっちかが入る
  3. ファイル指定 => Array、標準入力 => String
  4. if input_contents = Array => ファイル指定の時の個々の処理

    • each分の中で配列値をreadあるいはFile.read(配列変数)する
    • readしたものに対して行数、文字数、バイト数
    • ファイル名出力
  5. 標準入力の時の処理

    • 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(変数) => 配列が対象

コマンドライン引数か標準入力

  1. ARGF.file => ファイル指定で1ファイルのみ格納 文字列、 標準入力で#<IO:<STDIN>>が返ってくる
  2. ARGF.argv => ファイル指定で配列で取得、標準入力の場合 空
  3. ARGV => ファイル指定で配列で取得、標準入力の場合 空
  4. readlines => ファイル指定で指定ファイルの内容を改行ごとに配列格納。複数だと全内容を1要素として格納、標準入力での出力 = 改行含めて取得 "ファイル名\n…複数" この文字を配列として改行ごとに格納
  5. readline => 1行のみなのであまり使えない
  6. STDIN.gets => ファイル指定では動作が止まる 、標準入力での出力 = 先頭一つのみ取得"ファイル名\n" 文字列
  7. $stdin.read => ファイル指定では動作が止まる 、標準入力での出力 = 改行含めて取得 "ファイル名\n…複数" 文字列
  8. $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 を返します。

引用: class ARGF.class (Ruby 3.1 リファレンスマニュアル)

参考リンク

qiita.com

sites.google.com

逆引きRubyあった・・・