From: Stefano Crocco Date: 2007-09-19T23:27:48+09:00 Subject: Re: YAML & readlines & modify text files Alle mercoled� 19 settembre 2007, Dan George ha scritto: > Hello, > > Can anyone help me with the following issue: > > I have a YAML file that looks like this: > > --- > gm: Name01 > gs: N01 > --- > gm: Name02 > gs: N02 > > In ruby I'm trying to read all *.txt files in the current folder and > all sub-folders. The text files look like this: > > Name-line: Name01 > Link-line: blabla.something.N01&=bla > > Name-line: Name02 > Link-line: blabla.something.N02&=bla > > My ruby code looks like this: > > require 'yaml' > lnk = 'blabla.something.' > op = '&=bla' > > name = File.open('name.yaml') > yp = YAML::load_documents(name) do |name| > txt_files = Dir.glob('**/*.txt').each do |path| > file = File.open(path).readlines.each { |line| > > if line.match(/Link-line/) > then line.gsub!(/Link-line.*/, 'Link-line: '+ > lnk + name['gs'] + op) > end > } > > File.open(path, 'w'){|f| f.write file} > end > end > > > My problem is that the code replaces the YAML value 'gs' with the last > value found in the *.txt values. > > I want it to read the Name-line in each file and after that use the > appropriate 'gs' value from the YAML file in "line.gsub!(/Link- > line.*/, 'Link-line: '+ lnk + name['gs'] + op)" with the Link-line > field. > > I've been trying to find a way for some time now but I just can't seem > to be able to do it and I'm starting to have headaches :) so if anyone > has any ideas or improvements or critiques please don't hesitate to > reply. > > Cheers :) I'm not at all sure I understand correctly what you want to do. I think you want to replace the line under Name-line: Name01 with some text containing a string taken from the Name01 entry in the yaml file. Is it correct? If it isn't, then please explain better what you mean. Otherwise, read on. In my opinion, you're storing data in the YAML file in the wrong way, because, at each iteration, name contains only one pair of name/replacement string, which forces you to iterate over all the files for each document in the YAML file (and also makes the replacing code more complicated). I think your YAML file should contain a single hash, with the names as keys and the replacement strings as values: --- Name01: N01 Name02: N02 Then, you can do the following (untested) require 'yaml' lnk = 'blabla.something.' op = '&=bla' hash = File.open('name.yaml'){|f| YAML.load f} Dir.glob('**/.txt').each do |path| lines = File.readlines(path) lines.each_with_index do |line, i| if line.match(/Link-line/) match = lines[i-1].match(/Name-line:\s(.*)$/)[1] line.gsub!(/Link-line.*/, 'Link-line: '+ lnk+hash[match[1]]+ op) if match and hash.has_key?(match[1]) end end File.open(path,'w'){|f| f.write lines} end When iterating on the lines, the block is passed not only the line, but also the line number. This way, when you meet a Link-line line, you can access the corresponding name-line using its index. It then matches the previous line with a regexp to extract the name from it and stores the result in the match variable. If match is not nil (i.e if the name line had the expected format) and the name is included in hash, the replacement is performed (of course, you can skip this test if you're confident enough in the format of the files and in the contents of the yaml file) I hope this helps Stefano