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

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

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

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Ruby

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

Q&A

1回答

3402閲覧

Ruby CSVファイルを作成したいのですが、うまくいきません:ループ処理:書き込みモードと追記モードとリダイレクトで結果が異なる。

dlrowolleh

総合スコア120

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Ruby

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

0グッド

0クリップ

投稿2017/01/19 15:13

編集2017/01/22 13:02

###前提・実現したいこと
以下のファイルから、必要な部分だけを抜き出して、csvファイルを作成したいのですが、どのようにやればいいのでしょうか。

test.txt

1 <>不要<> 2 <>不要<> 3 <>不要<> 4 <>不要<>。 5 <>不要<>。 6 <>不要<> 7 <>不要<> 8 <>不要<> 9 <>不要<> 10 <>不要<> 11 <>不要<> 12 <>不要<> 13 <>不要<> 14 <>不要<> 15 <>不要<> 16 <>不要<> 17 <>不要<> 18 <>不要<> 19 <>不要<> 20 <>不要<> 21 <>不要<> 22 <>不要<> 23 <>不要<> 24 <>不要<> 25 ▸-▸-<>不要<> 26 ▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 27 ▸-▸-▸-▸-<>欲しい情報2017年<> 28 ▸-▸-▸-▸-<>欲しい情報01月<> 29 ▸-▸-▸-▸-<>欲しい情報19日<> 30 ▸-▸-▸-▸-<>欲しい情報15時間<> 31 ▸-▸-▸-▸- 32 ▸-▸-▸-▸-▸-<>不要<> 33 ▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 34 ▸-▸-▸-▸-<>不要<> 35 ▸-▸-▸-▸-<>不要<> 36 ▸-▸-▸-▸-<>不要<> 37 ▸-▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 38 ▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 39 ▸-▸-▸-▸-<>不要<> 40 ▸-▸-▸-▸-<>不要<> 41 ▸-▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 42 ▸-▸-▸-▸-▸-▸-▸-▸-<>欲しい情報<> 43 ▸-▸-▸-▸-<>不要<> 44 ▸-▸-▸-▸-<>欲しい情報A<> 45 ▸-▸-▸-▸-<>不要<> 46 ▸-▸-▸-▸-<>不要<> 47 ▸-▸-▸-▸-<>不要<> 48 ▸-▸-▸-▸-<>不要<> 49 ▸-▸-▸-▸-<>不要<> 50 ▸-▸-▸-▸-<>欲しい情報2017年<> 51 ▸-▸-▸-▸-<>欲しい情報01月<> 52 ▸-▸-▸-▸-<>欲しい情報19日<> 53 ▸-▸-▸-▸-<>欲しい情報14時間<> 54 ▸-▸-▸-▸- 55 ▸-▸-▸-▸-▸-<>不要<> 56 ▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 57 ▸-▸-▸-▸-<>不要<> 58 ▸-▸-▸-▸-<>不要<> 59 ▸-▸-▸-▸-<>不要<> 60 ▸-▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 61 ▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 62 ▸-▸-▸-▸-<>不要<> 63 ▸-▸-▸-▸-<>不要<> 64 ▸-▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 65 ▸-▸-▸-▸-▸-▸-▸-▸-<>欲しい情報<> 66 ▸-▸-▸-▸-<>不要<> 67 ▸-▸-▸-▸-<>欲しい情報A<> 68 ▸-▸-▸-▸-<>不要<> 69 ▸-▸-▸-▸-<>不要<> 70 ▸-▸-▸-▸-<>不要<> 71 ▸-▸-▸-<>不要<> 72 ▸-▸-▸-▸-▸-▸-▸-▸-<>不要<> 73 ▸-▸-▸-▸-<>欲しい情報2017年<> 74 ▸-▸-▸-▸-<>欲しい情報01月<> 75 ▸-▸-▸-▸-<>欲しい情報19日<> 76 ▸-▸-▸-▸-<>欲しい情報13時間<> ~以下繰り返し~

というファイルから、

data.csv

年,月,日,時間,情報,A, 欲しい情報2017年,欲しい情報01月,欲しい情報19日,欲しい情報15時,欲しい情報,欲しい情報A 欲しい情報2017年,欲しい情報01月,欲しい情報19日,欲しい情報15時,欲しい情報,欲しい情報A ~省略~

というようなCSV形式のファイルを新規作成したいです。
###試したこと

ruby

1 1 require "csv" 2 2 open('test.txt', 'r:utf-8') do |f| 3 3 # if f < 26 then 4 4 # next 5 5 # end 6 6 f.each_line do |line| 7 7 name = line[/>(.*)</,1] 8 8 puts name 9 9 end 10 10 end

不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 欲しい情報2017年 欲しい情報01月 欲しい情報19日 欲しい情報15時間 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 欲しい情報 不要 欲しい情報A 不要 不要 不要 不要 不要 欲しい情報2017年 欲しい情報01月 欲しい情報19日 欲しい情報14時間 不要 不要 不要 不要 不要 不要 不要 不要 不要 不要 欲しい情報 不要 欲しい情報A 不要 不要 不要 欲しい情報2017年 欲しい情報01月 欲しい情報19日 欲しい情報13時間

と出力されました。
欲しい情報は

  • 27行目に初めて出てくる(1)
  • 出てきてから(1)または(5)から4行下までの間出てくる(2)
  • (2)から12行下に出てくる(3)
  • (3)から2行下に出てくる(4)
  • (4)から6行下に出てくる(5)
  • (2)に戻り、ファイルの終わりまで繰り返す

と繰り返し出てきます。
ファイルから1行ずつ読み込んで処理したいので、

ruby

1 3 # if f < 26 then 2 4 # next 3 5 # end

とすると

foo.rb:3:in `block in <main>': undefined method `<' for #<File:test.txt (closed)> (NoMethodError) from foo.rb:2:in `open' from foo.rb:2:in `<main>'

と表示されました。
プログラムをどのように修正すればよいでしょうか。
ファイルの読み込み、書き出しの処理を回す処理がどのようになるのか分かりません。
###余談(解決済み)

ruby

1 1 require "csv" 2 2 open('test.txt', 'r:utf-8') do |f| 3 3 f.each_line do |line| 4 4 name = line[/>(.*)</,1] 5 5 put name 6 6 end 7 7 end

とすると

hoge.rb:5:in `block (2 levels) in <main>': undefined method `put' for main:Object (NoMethodError) from hoge.rb:3:in `each_line' from hoge.rb:3:in `block in <main>' from hoge.rb:2:in `open' from hoge.rb:2:in `<main>'

エラーが出ました。
なぜput
ではエラーになって
puts
ではエラーになるのですか?
putputsの違いは
改行して出力するか
そうでないか
だけではないのですか?

putではなくprintだったらうまく行きました。
putorintを勘違いしていました。

###追記
以下のプログラムで実行すると

Ruby

1require "csv" 2 3open("test.txt", "r:utf-8") do |f| 4 puts ["年","月","日","時間","情報","A"].to_csv 5 26.times { f.gets } 6 cnt = 0 7 out = nil 8 f.each_line do |line| 9 cnt += 1 10 case cnt 11 when 1 12 name = line[/>(.*)</,1] 13 out = [name] 14 when 2,3,4,16 15 name = line[/>(.*)</,1] 16 out << name 17 when 18 18 name = line[/>(.*)</,1] 19 out << name 20 puts out.to_csv 21 when 23 22 cnt = 0 23 end 24 end 25end

$ ruby csv.rb

年,月,日,時間,情報,A,
欲しい情報2017年,欲しい情報01月,欲しい情報19日,欲しい情報15時,欲しい情報,欲しい情報A
欲しい情報2017年,欲しい情報01月,欲しい情報19日,欲しい情報15時,欲しい情報,欲しい情報A
省略

端末に表示されます。

$ ruby csv.rb > data.csv

で端末への出力をファイルdata.csvへ書き込むことで
data.csv
が作成されるのですが、

$ ruby csv.rb

data.csvで実現するにはどうしたらいいでしょうか。

csv.rb

に変更したところ、エラーが出てしまっており、困っています。

Ruby

1 1 require "csv" 2 2 3 3 open("test.txt", "r:utf-8") do |f| 4 4 HEADER = ["年","月","日","時間","情報","A"].to_csv 5 5 26.times { f.gets } 6 6 cnt = 0 7 7 out = nil 8 8 f.each_line do |line| 9 9 cnt += 1 10 10 case cnt 11 11 when 1 12 12 name = line[/>(.*)</,1] 13 13 out = [name] 14 14 when 2,3,4,16 15 15 name = line[/>(.*)</,1] 16 16 out << name 17 17 when 18 18 18 name = line[/>(.*)</,1] 19 19 out << name 20 20 CSV.open("hoge.html","a:Windows-31J", headers => HEADER, :write_headers => true) do |file| 21 21 file << out.to_csv 22 22 end 23 23 when 23 24 24 cnt = 0 25 25 end 26 26 end 27 27 end
data.rb:20:in `block (2 levels) in <main>': undefined local variable or method `headers' for main:Object (NameError) from data.rb:8:in `each_line' from data.rb:8:in `block in <main>' from data.rb:3:in `open' from data.rb:3:in `<main>'

#追記 ループ処理が分からない
#a(追記モード)とw(上書きモード)で結果が異なる

w-mode.rb(上書きモード)

Ruby

1 1 require "csv" 2 2 3 3 open("utf-8.html", "r:utf-8") do |f| 4 4 HEADER = ["年","月","日","時間","情報","A"] 5 5 26.times { f.gets } 6 6 cnt = 0 7 7 out = nil 8 8 f.each_line do |line| 9 9 cnt += 1 10 10 case cnt 11 11 when 1 12 12 name = line[/>(.*)</,1] 13 13 out = [name] 14 14 when 2,3,4,16 15 15 name = line[/>(.*)</,1] 16 16 out << name 17 17 when 18 18 18 name = line[/>(.*)</,1] 19 19 out << namew 20 20 when 23 21 21 cnt = 0 22 22 end 23 23 CSV.open("w-mode.csv","w:utf-8", 7,,,時間,情報,A 24 8 2017,01,21,23 25 9,,,時間,情報,A 26:headers => HEADER, :write_headers => true) do |file| 27 24 file << out 28 25 end 29 26 end 30 27 end

作成されたファイルの中身

w-mode.csv

1 年,月,日,時間,情報,A 2 ,,"",

a-mode.rb(追記書きモード)

Ruby

1 1 require "csv" 2 2 3 3 open("utf-8.html", "r:utf-8") do |f| 4 4 HEADER = ["年","月","日","時間","情報","A"] 5 5 26.times { f.gets } 6 6 cnt = 0 7 7 out = nil 8 8 f.each_line do |line| 9 9 cnt += 1 10 10 case cnt 11 11 when 1 12 12 name = line[/>(.*)</,1] 13 13 out = [name] 14 14 when 2,3,4,16 15 15 name = line[/>(.*)</,1] 16 16 out << name 17 17 when 18 18 18 name = line[/>(.*)</,1] 19 19 out << namew 20 20 when 23 21 21 cnt = 0 22 22 end 23 23 CSV.open("a-mode.csv","a:utf-8", 7,,,時間,情報,A 24 8 2017,01,21,23 25 9,,,時間,情報,A 26:headers => HEADER, :write_headers => true) do |file| 27 24 file << out 28 25 end 29 26 end 30 27 end

a-mode.csv

1 年,月,日,時間,情報,A(ヘッダ部) 2 欲しい情報2017年 3 年,月,日,時間,情報,A(ヘッダ部) 4 欲しい情報2017年,欲しい情報01月 5 年,月,日,時間,情報,A(ヘッダ部) 6 欲しい情報2017年,欲しい情報01月,欲しい情報21時間 7 年,月,日,時間,情報,A(ヘッダ部) 8 欲しい情報2017年,欲しい情報01月,欲しい情報21時間,欲しい情報A 〜省略〜

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

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

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

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

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

guest

回答1

0

一旦、readlinesで全部Arrayに読み込んでしまうのが簡単と思いますが、ループしながら読むのだとこんな感じで。

Ruby

1require "csv" 2 3open("test.txt", "r:utf-8") do |f| 4 puts ["年","月","日","時間","情報","A"].to_csv 5 26.times { f.gets } 6 cnt = 0 7 out = nil 8 f.each_line do |line| 9 cnt += 1 10 case cnt 11 when 1 12 name = line[/>(.*)</,1] 13 out = [name] 14 when 2,3,4,16 15 name = line[/>(.*)</,1] 16 out << name 17 when 18 18 name = line[/>(.*)</,1] 19 out << name 20 puts out.to_csv 21 when 23 22 cnt = 0 23 end 24 end 25end

if f < 26がエラーになるのは、fはファイル(IOクラス)でこれは数値と大小比較できないからです。

putがエラーになるのは、putというメソッドが存在しないからです。なぜputと書こうと思ったのでしょうか?

投稿2017/01/20 02:11

otn

総合スコア84380

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

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

dlrowolleh

2017/01/20 05:54

ご回答いただきありがとうございます。 実行すると、 想定していた出力が得られました! open('test.txt', 'r:utf-8') do |f|で fがファイルの行をカウントするものだと思っていました。 ファイルの先頭の行(1行目)を読み込む時に、fに1が入り、 2行目読み込んでいる時はfが2 3行目の時はfが3… とカウントしていき、最終行になるまで続けているから f<26のときはファイルを読み込まない、という処理にすればいいと、よく分かっていませんでした。 >putがエラーになるのは、putというメソッドが存在しないからです。 putがが改行せずに出力するメソッドだと思っていました。 改行せずに出力するメソッドはprintでした。 ずっと勘違いしていました。 5行目をputにして表示が変化するのかを確かめたらなにか分かるかもしれないと思いやってみようと思いました。 混乱していました。
otn

2017/01/20 08:53

追記に対しては、最小限の修正で行くと、2箇所の.to_csvを取って、headers を :headers に。 エラーメッセージを見ても、: の漏れに気づかないとは、プログラミングのABCがまだわかっていませんね。 あと、ちゃんと改善するなら、CSV.openは、ループの外で"w"指定で。
dlrowolleh

2017/01/22 12:06

ありがとうございます。 2箇所の.to_csvを取って、headers を :headers にしたところ、実行はできるようになりましたが、 実行結果は想定していたものではありませんでした。 >CSV.openは、ループの外で"w"指定で。 "a"は追記、 "w"は上書き というのは分かります。 >ループの外 というのはどこに当たる部分になるのでしょうか。 いろいろとファイルに書き込む処理の場所を実行してみたのですが、分かりませんでした。 また、 作成したファイルをexcel等で計算、グラフ処理等をしたいのでデータを蓄積したいと考えています。 なので前のデータに上書きしないようにと思っているのですが そういった場合は追記モード"a"でも大丈夫でしょうか? 度々申し訳ありません。
otn

2017/01/22 13:15

ループの外というのは、f.each_line の外側です。 過去データに追記するなら、"a"でいいです。 であれば、ヘッダは書かないようにした方が良いのでは?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.51%

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

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

質問する

関連した質問