1

I'm having some trouble figuring out how to 1) traverse a directory and 2) taking each file (.txt) and saving it as a string. I'm obviously pretty new to both ruby and rails.

I know that I could save the file with f=File.open("/path/*.txt") and then output it with puts f.read but I would rather save it as a string, not .txt, and dont know how to do this for each file.

Thanks!

Rymo4
  • 461
  • 2
  • 7
  • 15

4 Answers4

3

You could use Dir.glob and map over the filenames to read each filename into a string using IO.read. This is some pseudo code:

file_names_with_contents = Dir.glob('/path/*.txt').inject({}){|results, file_name| result[file_name] = IO.read(file_name)}
Jake Dempsey
  • 6,264
  • 1
  • 30
  • 25
  • I don't really understand this. what does this return/how does this work? so far i have(sorry im not sure how to get it on a new line): `Find.find('/path/') do |f| file=File.open(f) text="" file.each {|line| text << line } Essay.create!(:body => text) end` – Rymo4 May 29 '11 at 07:13
2

The following based on python os.walk function, which returns a list of tuples with: (dirname, dirs, files ). Since this is ruby, you get a list of arrays with: [dirname, dirs, files]. This should be easier to process than trying to recursively walk the directory yourself. To run the code, you'll need to provide a demo_folder.

def walk(dir)
  dir_list = []
  def _walk(dir, dir_list)
    fns = Dir.entries(dir)
    dirs = []
    files = []
    dirname = File.expand_path(dir)
    list_item = [dirname, dirs, files]
    fns.each do |fn|
      next if [".",".."].include? fn
      path_fn = File.join(dirname, fn)
      if File.directory? path_fn
        dirs << fn
        _walk(path_fn, dir_list)
      else
        files << fn
      end
    end
    dir_list << list_item
  end

  _walk(dir, dir_list)
  dir_list
end

if __FILE__ == $0

  require 'json'

  dir_list = walk('demo_folder')
  puts JSON.pretty_generate(dir_list)
end
fbeshears
  • 61
  • 4
2

You could prob also use tap here:

file_names_with_contents = {}.tap do |h|
  Dir.glob('/path/*.txt').each{|file_name| h[file_name] = IO.read(file_name)}
end
Jake Dempsey
  • 6,264
  • 1
  • 30
  • 25
1

Jake's answer is good enough, but each_with_object will make it slightly shorter. I also made it recursive.

def read_dir dir
  Dir.glob("#{dir}/*").each_with_object({}) do |f, h|
    if File.file?(f)
      h[f] = open(f).read
    elsif File.directory?(f)
      h[f] = read_dir(f)
    end
  end
end

When the directory is like:

--+ directory_a
  +----file_b
  +-+--directory_c
  | +-----file_d
  +----file_e

then

read_dir(directory_a)

willl return:

{file_b => contents_of_file_b,
 directory_c => {file_d => contents_of_file_d},
 file_e => contents_of_file_e}
sawa
  • 165,429
  • 45
  • 277
  • 381
  • if you do a Dir.glob with ** doesnt it give you all the files in all child dirs? I had that in my original solution, but he asked for /path/*.txt so I just used that pattern. – Jake Dempsey May 31 '11 at 21:17