views:

82

answers:

3

Assume, i got the following file (input.txt):

name = "Peter"
age = 26
family_status = married

Mentioned lines can be stored in a random order, lie:

family_status = married
name = "Peter"
age = 26

In my program I also have variables family_status, age and name. How do I in a single cycle read those lines from file and assign correspond variables with the values?

A: 

You can try this. Not pretty on a single line though:

class Test
  attr_accessor :name, :age, :family_status

  def load
    File.foreach('input.txt') do |line|
      match = line.match /^(\S+)\s*=\s*"?(\S+?)"?\s*$/
      self.send("#{match[1]}=", match[2]) if match
    end
  end
end

test = Test.new
test.load
Vincent
It is not advisable to eval input from external sources. In your case, your input may call unexpected methods.You are not even assining variables with your code. You are calling methods. This works only for public writeable attributes, which may not always be the way it's wanted.I assume the questioner does not want all variables to be strings. With your code, you only create strings.
johannes
+3  A: 

Setting Variables

This depends on several facts.

  • What kind of variables do you have (local variables, instance variables, class variables, or global variables)
  • What kind of type is family_status (String, Symbol, whatever)

I assume you are using instance variables for this:

def read_vars(io, vars)
  io.each do |line|
    # ensure the line has the right format (name = var)
    raise "wrong format" unless line=~ /^\s*(\w+)\s*=\s*"?(.*?)"?\s+$/
    var= :"@#{$1}"
    # pick the type of the data
    value= case vars[var]
    when String
      $2
    when Integer
      $2.to_i
    when Symbol
      $2.to_sym
    else
      raise "invalid var"
    end
    instance_variable_set(var, value)
  end
end

read_vars(File.read("input.txt", :@age => Integer, :@name => String, :@family_status => Symbol )

If you are not using instance variables you have to change the instacne_variable_set and var= :"@... line to you needs. This code has the following advantages:

  • You control which variables can be set
  • You control which types these variables have
  • You can easily add new variables and/or change types of variables without changing the read code
  • You can use this code to read entirely different files, without writing new code for it

reading as YAML

If your needs are not as specific as in your question I would go an entirely different approach to this.

I would write the input.txt as a yaml file. In yaml syntax it would look like this:

---
name: Peter
age: 26
family_status: :married

You can read it with:

YAML.load(File.read("input.txt")) # => {"name" => "Peter", "age" => 26, "family_status" => :married }

Be carefull if you don't control the input.txt file, you don't control which types the data will have. I would name the file input.yaml instead of input.txt. If you want to know more, about how to write yaml files have a look at: http://yaml.kwiki.org/?YamlInFiveMinutes. More infos about yaml and ruby can be found at http://www.ruby-doc.org/stdlib/libdoc/yaml/rdoc/index.html.

johannes
+2  A: 

Assuming the file is really as regular as your example, you can slurp everything into a nice hash like this:

input = IO.read("input.txt")
input_hash = Hash[*input.gsub(/"/,"").split(/\s*[\n=]\s*/)]

That gives you:

=> {"name"=>"Peter", "family_status"=>"married", "age"=>"26"}

But then you're really going to need some variable-specific code to do whatever type-handling you want, particularly for whatever family_status is...

glenn mcdonald