Ruby ファイルを分けて実行すると上手くのに、ファイルを一緒にしたりrequireするとエラーがでる
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 865
文字列を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ページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
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
だったらそれは省略できます)
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.20%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/02/25 18:39
ちなみに、`File.open('C:\pg\uni_sort\uni_replace.rb','a').puts address.sort`ですが、仕様です。__END__以下に追記で出力されるので、スクリプトには影響ありません。ほんとはアウトプットはファイルを分けるべきだとは知っていますが、自分用のものを作る際はこういう方が結果の確認が楽なのでこういう書き方してます。
2018/02/25 18:49
a オプションとか,気づかなかった。