質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.20%

Ruby ファイルを分けて実行すると上手くのに、ファイルを一緒にしたりrequireするとエラーがでる

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 865

igar

score 98

文字列をCSVに整形し、それを数値でソートする、というものなのですが、ファイルを別々に実行するのが面倒なので一つのファイルで二つ目のファイルをrequireでまとめて実行しようとしたところ、後述のようなエラーがでてしまいます。どうしたらよいでしょうか?

ファイル構成

.
├── csv_sort.rb
├── output.csv
└── uni_replace.rb

C:\pg\uni_sort\csv_sort.rb

Encoding.default_external = 'UTF-8'

class Address
  # http://somethingpg.hatenablog.com/entry/20111218/1324176083
 def initialize
  buf = Array.new #バッファするための配列を宣言
  open('C:/pg/uni_sort/output.csv',"r") do |file|
    file.each_line do |s|
    buf << s.chomp #改行文字\nを取り除きつつbufに値を格納
    @s = buf #できあがった配列をインスタンス変数に格納
    end
  end
end

 def sort #降順に並べ替える
    @s.sort! do |one,another|
    one = one.split(',')
    another = another.split(',')
    one[2] <=> another[2]
    end
 end
end

address = Address.new
puts address.sort
File.open('C:\pg\uni_sort\uni_replace.rb','a').puts address.sort

C:\pg\uni_sort\uni_replace.rb

Encoding.default_external = 'UTF-8'

@source='

紅華刑
HIGH SCORE:1,006,988

Tidal Wave
HIGH SCORE:1,004,862

BOKUTO
HIGH SCORE:1,008,541

We Gonna Party -Feline Groove Mix-
HIGH SCORE:1,002,045

おまかせ!!トラブルメイ娘☆とれびちゃん
HIGH SCORE:1,006,875


'

@source.gsub!(/(.*?)\n(.*?)\n\n/,"\\1,\\2\n")
@source.gsub!(/HIGH SCORE:/,"")
@source.gsub!(/^\n*(.*?)/,"\\1")

puts @source
File.open('C:\pg\uni_sort\output.csv','w').puts @source


require 'C:\pg\uni_sort\csv_sort.rb'



__END__

C:\pg\uni_sort\uni_replace.rbの期待する出力

We Gonna Party -Feline Groove Mix-,1,002,045
Tidal Wave,1,004,862
おまかせ!!トラブルメイ娘☆とれびちゃん,1,006,875
紅華刑,1,006,988
BOKUTO,1,008,541

C:\pg\uni_sort\uni_replace.rbの実行結果

C:/pg/uni_sort/csv_sort.rb:17:in `sort': undefined method `sort!' for nil:NilClass (NoMethodError)
    from C:/pg/uni_sort/csv_sort.rb:26:in `<top (required)>'
    from C:/Ruby24-x64/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from C:/Ruby24-x64/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
    from C:/pg/uni_sort/uni_replace.rb:32:in `<main>'
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

uni_replace.rb において,

File.open('C:\pg\uni_sort\output.csv','w').puts @source

という行がありますね。

ファイルを開いておいてそのファイルに @source を書き出しているのですが,ファイルを閉じていません。

ファイルへの書き込みというのは,puts した瞬間に完了するわけではなくて,いちどバッファーにたまったものが,あるタイミングで実際にファイルに書かれてゆくんです。
なので,上記のコードを実行した直後に,実際にファイルに書かれているかどうかは分かりません。
というか,いまのケースでは 1 字も書かれていないんですよ。

で,このファイル close してませんよね。close すれば書き込みを完了してくれます。

そのためには,上のコードを

file = File.open('C:\pg\uni_sort\output.csv','w')
file.puts @source
file.close

とすればいいんですが,Ruby ではふつうこんな書き方はしません。もっといい方法があるからです。

それは open 時にブロックを使う方法で

File.open('C:\pg\uni_sort\output.csv','w') do |file|
  file.puts @source
end

とします。
ブロック付きで File.open を呼び出した場合,ブロックの評価が終わったところで自動的にファイルは close されます。
プログラマーが「ファイルのクローズ」って意識しなくていいんです。
たとえブロックの実行中にエラーが起こって処理が中断してもクローズしてくれます。

ところで,もっと簡潔な方法もあります。

File.write 'C:\pg\uni_sort\output.csv', @source

です。
ただ,完全に等価というわけではありません。puts は改行を補いますが,File.write は与えられたものだけを書き込むからです。


以上が回答ですが,コードを拝見して気になったことを。

まず Address クラスの initialize メソッドが

def initialize
  buf = Array.new #バッファするための配列を宣言
  open('C:/pg/uni_sort/output.csv',"r") do |file|
    file.each_line do |s|
      buf << s.chomp #改行文字\nを取り除きつつbufに値を格納
      @s = buf #できあがった配列をインスタンス変数に格納
    end
  end
end

となっていますね。

Array.new は単に [] と書けます。

それから,

open(ファイルパス, "r") do |file|
  file.each_line do |s|
    # 云々
  end
end

IO.foreach(ファイルパス) do |line|
  # 云々
end

と書けます。

行をあとで chomp するなら,最初から

IO.foreach(ファイルパス, chomp: true) do |line|
  # 云々
end

で OK。ただし,chomp オプションが導入されたのは Ruby 2.4 です。

また,each_line のループの中で毎回

@s = buf

していますが,これはメソッドの末尾でいいですね。

というか,最初から @s = [] としておけば,buf なんて要りません。

以上をまとめると

def initialize
  @s = []
  IO.foreach('C:/pg/uni_sort/output.csv', chomp: true) do |line|
    @s << line
  end
end

でいいわけです。

と,ここまで書いて気付きましたが,これって単に

def initialize
  @s = IO.readlines('C:/pg/uni_sort/output.csv', chomp: true)
end

でいいんじゃん。

次に

def sort #降順に並べ替える
  @s.sort! do |one,another|
    one = one.split(',')
    another = another.split(',')
    one[2] <=> another[2]
  end
end

ですが,one にも another にも同じ変換を施すのは無駄です。sort_by! を使って

def sort
  @s.sort_by! do |one|
    one.split(',')[2]
  end
end

としましょう。

ファイル末尾の

File.open('C:\pg\uni_sort\uni_replace.rb','a').puts address.sort

は何かの間違いですよね? スクリプトを書き換えちゃってますが。

次に uni_replace.rb ですが,トップレベルで @source とインスタンス変数を用いる必然性はないので,ローカル変数 source でいいでしょう。

同じディレクトリーのファイルを require するのは

require_relative 'csv_sort'

などと書けます。(ちなみに,require も require_relative も,ファイルの拡張子が .rb だったらそれは省略できます)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/02/25 18:39

    なるほど、closeしないとこういう弊害もあるのですね。
    ちなみに、`File.open('C:\pg\uni_sort\uni_replace.rb','a').puts address.sort`ですが、仕様です。__END__以下に追記で出力されるので、スクリプトには影響ありません。ほんとはアウトプットはファイルを分けるべきだとは知っていますが、自分用のものを作る際はこういう方が結果の確認が楽なのでこういう書き方してます。

    キャンセル

  • 2018/02/25 18:49

    おおー,そういうことでしたか!(スクリプトファイルに書き込む件)
    a オプションとか,気づかなかった。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.20%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • Rubyに関する質問
  • Ruby ファイルを分けて実行すると上手くのに、ファイルを一緒にしたりrequireするとエラーがでる