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

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

ただいまの
回答率

88.10%

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

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,680

score 120

前提・実現したいこと

以下のファイルから、必要な部分だけを抜き出して、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
ではエラーになるのですか?
putputsの違いは
改行して出力するか
そうでないか
だけではないのですか?

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

追記

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

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 欲しい情報20173 年,月,日,時間,情報,A(ヘッダ部)
      4 欲しい情報2017年,欲しい情報015 年,月,日,時間,情報,A(ヘッダ部)
      6 欲しい情報2017年,欲しい情報01月,欲しい情報21時間
      7 年,月,日,時間,情報,A(ヘッダ部)
      8 欲しい情報2017年,欲しい情報01月,欲しい情報21時間,欲しい情報A
〜省略〜
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

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と書こうと思ったのでしょうか?

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 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

    追記に対しては、最小限の修正で行くと、2箇所の.to_csvを取って、headers を :headers に。
    エラーメッセージを見ても、: の漏れに気づかないとは、プログラミングの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

    ループの外というのは、f.each_line の外側です。

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

    キャンセル

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

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

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

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