Ruby CSVファイルを作成したいのですが、うまくいきません:ループ処理:書き込みモードと追記モードとリダイレクトで結果が異なる。
- 評価
- クリップ 0
- VIEW 2,680
前提・実現したいこと
以下のファイルから、必要な部分だけを抜き出して、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形式のファイルを新規作成したいです。
試したこと
1 require "csv"
2 open('test.txt', 'r:utf-8') do |f|
3 # if f < 26 then
4 # next
5 # end
6 f.each_line do |line|
7 name = line[/>(.*)</,1]
8 puts name
9 end
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行ずつ読み込んで処理したいので、
3 # if f < 26 then
4 # next
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>'
と表示されました。
プログラムをどのように修正すればよいでしょうか。
ファイルの読み込み、書き出しの処理を回す処理がどのようになるのか分かりません。
余談(解決済み)
1 require "csv"
2 open('test.txt', 'r:utf-8') do |f|
3 f.each_line do |line|
4 name = line[/>(.*)</,1]
5 put name
6 end
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
ではエラーになるのですか?
put
とputs
の違いは
改行して出力するか
そうでないか
だけではないのですか?
↑
put
ではなくprint
だったらうまく行きました。
put
とorint
を勘違いしていました。
追記
以下のプログラムで実行すると
require "csv"
open("test.txt", "r:utf-8") do |f|
puts ["年","月","日","時間","情報","A"].to_csv
26.times { f.gets }
cnt = 0
out = nil
f.each_line do |line|
cnt += 1
case cnt
when 1
name = line[/>(.*)</,1]
out = [name]
when 2,3,4,16
name = line[/>(.*)</,1]
out << name
when 18
name = line[/>(.*)</,1]
out << name
puts out.to_csv
when 23
cnt = 0
end
end
end
$ 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
に変更したところ、エラーが出てしまっており、困っています。
1 require "csv"
2
3 open("test.txt", "r:utf-8") do |f|
4 HEADER = ["年","月","日","時間","情報","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 CSV.open("hoge.html","a:Windows-31J", headers => HEADER, :write_headers => true) do |file|
21 file << out.to_csv
22 end
23 when 23
24 cnt = 0
25 end
26 end
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(上書きモード)
1 require "csv"
2
3 open("utf-8.html", "r:utf-8") do |f|
4 HEADER = ["年","月","日","時間","情報","A"]
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 << namew
20 when 23
21 cnt = 0
22 end
23 CSV.open("w-mode.csv","w:utf-8", 7 年,月,日,時間,情報,A
8 2017,01,21,23
9 年,月,日,時間,情報,A
:headers => HEADER, :write_headers => true) do |file|
24 file << out
25 end
26 end
27 end
作成されたファイルの中身
w-mode.csv
1 年,月,日,時間,情報,A
2 ,,"",
a-mode.rb(追記書きモード)
1 require "csv"
2
3 open("utf-8.html", "r:utf-8") do |f|
4 HEADER = ["年","月","日","時間","情報","A"]
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 << namew
20 when 23
21 cnt = 0
22 end
23 CSV.open("a-mode.csv","a:utf-8", 7 年,月,日,時間,情報,A
8 2017,01,21,23
9 年,月,日,時間,情報,A
:headers => HEADER, :write_headers => true) do |file|
24 file << out
25 end
26 end
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
〜省略〜
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
0
一旦、readlines
で全部Arrayに読み込んでしまうのが簡単と思いますが、ループしながら読むのだとこんな感じで。
require "csv"
open("test.txt", "r:utf-8") do |f|
puts ["年","月","日","時間","情報","A"].to_csv
26.times { f.gets }
cnt = 0
out = nil
f.each_line do |line|
cnt += 1
case cnt
when 1
name = line[/>(.*)</,1]
out = [name]
when 2,3,4,16
name = line[/>(.*)</,1]
out << name
when 18
name = line[/>(.*)</,1]
out << name
puts out.to_csv
when 23
cnt = 0
end
end
end
if f < 26
がエラーになるのは、f
はファイル(IOクラス)でこれは数値と大小比較できないからです。
put
がエラーになるのは、put
というメソッドが存在しないからです。なぜput
と書こうと思ったのでしょうか?
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.10%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2017/01/20 14: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にして表示が変化するのかを確かめたらなにか分かるかもしれないと思いやってみようと思いました。
混乱していました。
2017/01/20 17:53
エラーメッセージを見ても、: の漏れに気づかないとは、プログラミングのABCがまだわかっていませんね。
あと、ちゃんと改善するなら、CSV.openは、ループの外で"w"指定で。
2017/01/22 21:06
2箇所の.to_csvを取って、headers を :headers にしたところ、実行はできるようになりましたが、
実行結果は想定していたものではありませんでした。
>CSV.openは、ループの外で"w"指定で。
"a"は追記、
"w"は上書き
というのは分かります。
>ループの外
というのはどこに当たる部分になるのでしょうか。
いろいろとファイルに書き込む処理の場所を実行してみたのですが、分かりませんでした。
また、
作成したファイルをexcel等で計算、グラフ処理等をしたいのでデータを蓄積したいと考えています。
なので前のデータに上書きしないようにと思っているのですが
そういった場合は追記モード"a"でも大丈夫でしょうか?
度々申し訳ありません。
2017/01/22 22:15
過去データに追記するなら、"a"でいいです。
であれば、ヘッダは書かないようにした方が良いのでは?