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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

解決済

1回答

216閲覧

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

igar

総合スコア110

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2018/02/24 10:42

文字列を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>'

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

uni_replace.rb において,

rb

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

という行がありますね。

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

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

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

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

rb

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

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

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

rb

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

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

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

rb

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

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


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

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

rb

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

となっていますね。

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

それから,

rb

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

rb

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

と書けます。

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

rb

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

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

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

rb

1@s = buf

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

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

以上をまとめると

rb

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

でいいわけです。

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

rb

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

でいいんじゃん。

次に

rb

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

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

rb

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

としましょう。

ファイル末尾の

rb

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

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

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

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

rb

1require_relative 'csv_sort'

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

投稿2018/02/24 11:33

scivola

総合スコア2108

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

igar

2018/02/25 09:39

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

2018/02/25 09:49

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問