How do I iterate over a file?

I have a file that looks like this:

[noahc:~/projects/wordsquares] master(+87/-50)* ± cat wordlist/test_word_list.txt
card
apple
joe
bird
card
dart
area
rear
birdbird
after
boat
swim
north
abbe
byes
beep

If I open up an irb session, I can do:

2.0.0p247 :001 > file = File.open('./wordlist/test_word_list.txt', 'r')
 => #<File:./wordlist/test_word_list.txt>
2.0.0p247 :002 > file.readlines
 => ["card\n", "apple\n", "joe\n", "bird\n", "card\n", "dart\n", "area\n", "rear\n", "birdbird\n", "after\n", "boat\n", "swim\n", "north\n", "abbe\n", "byes\n", "beep \n", "\n"]

But, now I have a class called WordSquareGenerator:

class WordSquareGenerator
  require 'pry'
  require 'pry-nav'
  require './lib/word_list_builder.rb'

  def initialize(n, file_location)
    @size_of_square = n
    @file = load_file(file_location)
    @word_stem_hash = WordListBuilder.new(n, @file).word_stem_hash
    @word_list = nil
  end

  def word_square_word_list
    binding.pry
    @file.each do |w|
      binding.pry
      @word_list ? break : solve_for_word_list([word])
    end
    binding.pry
  end

  def is_list_valid?(list)
    (0..@size_of_square - 1).each do |n|
      (0..@size_of_square - 2).each do |m|
        return false if list[n][m] != list [m][n]
      end
     end
    @generated_list = list unless @generated_list
  end

  def solve_for_word_list(word_array)
    if word_array.length == 4
      @word_list = word_array
    elsif @word_list
    else
      next_words = @word_stem_hash[word_array.map{|w| w[word_array.length]}.join]
      next_words.each do |word|
        solve_for_word_list(word_array + [word])
      end
    end
  end

private

  def load_file(file_location)
    File.open(file_location, 'r')
  end
end

When I run the word_square_word_list method and hit the first binding.pry, I can do:

2.0.0 (#<WordSquareGenerator:0x007ff3f91c16b0>):0 > @file.readlines
=> []

and I get an empty array for readlines. How can it be that I'm getting two different results doing the same thing, except one is inside that class and the other isn't?

Answers


At WordListBuilder you are probably reading the file already and when the action gets back to WordSquareGenerator the file is already at it's end and there's nothing else to read. First, don't do what you're doing now, that is opening the file since this leaks the file handle (you're not closing it anywhere) and someone else reading the handle is causing your code to fail.

Here's how you could do it:

class WordSquareGenerator
  require 'pry'
  require 'pry-nav'
  require './lib/word_list_builder.rb'

  def initialize(n, file_location)
    @size_of_square = n
    @file_location = file_location
    @word_stem_hash = WordListBuilder.new(n, file_location).word_stem_hash
    @word_list = nil
  end

  def word_square_word_list
    binding.pry
    IO.foreach(@file_location) do |word|
      binding.pry
      @word_list ? break : solve_for_word_list([word])
    end
    binding.pry
  end

  def is_list_valid?(list)
    (0..@size_of_square - 1).each do |n|
      (0..@size_of_square - 2).each do |m|
        return false if list[n][m] != list [m][n]
      end
     end
    @generated_list = list unless @generated_list
  end

  def solve_for_word_list(word_array)
    if word_array.length == 4
      @word_list = word_array
    elsif @word_list
    else
      next_words = @word_stem_hash[word_array.map{|w| w[word_array.length]}.join]
      next_words.each do |word|
        solve_for_word_list(word_array + [word])
      end
    end
  end

end

And you would also need to update WordListBuilder to do the same. This also has the advantage of closing the file handle automatically for you so you don't have to care about closing it yourself.


Try running this before you read the lines:

@file.seek 0

You have probably already read the lines, and you have to seek back to the start of the file before you read them again.

Sample IRB session:

irb(main):001:0> f = File.new 'test.txt' # already existing file
=> #<File:test.txt>
irb(main):002:0> f.readlines
=> ["this", "is", "a", "test"]
irb(main):003:0> f.readlines
=> []
irb(main):004:0> f.seek 0
=> 0
irb(main):005:0> f.readlines
=> ["this", "is", "a", "test"]

You could even make it a method:

def readlines
  @file.seek 0
  @file.readlines
end

Also, make sure to close your file (@file.close)! You should only leave it open for as little time as possible. And you definitely should not leave it open for the whole program. If you don't want to worry about that, just store the lines in a variable instead of keeping the file:

@lines = File.open(file_location) {|f|
  f.readlines
}
# you could also use the shortcut form
# @lines = File.open(file_location, &:readlines)

Need Your Help

How to export database from Oracle Application Express

oracle oracle-apex ddl

Really would appreciate any help whatsoever on this one.

WCF service httpcfg issue

c# wcf wcf-endpoint httpcfg.exe

We currently have a situation where we have a WCF service which generates custom excel reports for clients. This service is hosted in a windows service and uses net.tcp bindings.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.