From: Dan George Date: 2007-09-20T06:25:04+09:00 Subject: Re: YAML & readlines & modify text files On Sep 19, 11:03 pm, Stefano Crocco wrote: > Alle mercoled� 19 settembre 2007, Dan George ha scritto: > > > > > On Sep 19, 9:22 pm, Stefano Crocco wrote: > > > Alle mercoled� 19 settembre 2007, Dan George ha scritto: > > > > On Sep 19, 5:27 pm, Stefano Crocco wrote: > > > > > Alle mercoled� 19 settembre 2007, Dan George ha scritto: > > > > > > 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 > > > > > You are correct, that is what I want! > > > > > I tryied what you suggested but I get some weird errors: > > > > > new.rb:45: undefined method `[]' for nil:NilClass (NoMethodError) > > > > from D:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in > > > > `each_with_index' > > > > from new.rb:43:in `each' > > > > from new.rb:43:in `each_with_index' > > > > from new.rb:43 > > > > from new.rb:41:in `each' > > > > from new.rb:41 > > > > > Line 45 is: match = lines[i-1].match(/Name-line:\s(.*)$/)[1] > > > > Line 43 is: lines.each_with_index do |line, i| > > > > Line 41 is: Dir.glob('**/*.jad').each do |path| > > > > > I don't get it, as far as I can tell the code is ok... > > > > I think it's because of a mistake in my code: the [1] part of line 45 > > > shouldn't be there (it's a leftover from a previous version of the code). > > > If I'm right, the string doesn't match the regexp, so > > > lines[i-1].match(...) returns nil, which doesn't have a [] method, > > > leading to the error you get. Avod to call nil.[] is the reason for the > > > conditional at the end of the following line (note that the conditional > > > checks that match is not nil before trying to extract an element from > > > it), but this is useless if I call it on the line before. Removing that > > > [1] from line 45 should solve your porblem. > > > > However, if you found this mistake, it may mean that there's something > > > amiss in either your .txt files or your yaml file (see the end of my > > > previous post). > > > > Stefano > > > Stefano thanks for your replies so far. > > > That did. The code executes ok but "match = lines[i-1].match(/Name- > > line:\s(.*)$/)" always returs nil (I did "puts match" after it) and I > > don't understand why. I checked my txt files and my yaml file and they > > are ok. > > > txt file has: > > > Name-line: New York > > Link-line: etc > > > and YAML file has: > > > --- > > New York: NY > > > So the value from the Name is equal to the one in the YAML file. Am I > > still missing something obvious? > > > Sorry if some things seem so obvious that I should understand them but > > I'm just a beginner, started a couple of weeks back and the only way > > for me to learn is from examples that I try myself and it annoys to > > see that something simple gives me so much trouble but I don't want to > > quit either :). > > There's nothing obvious in the problem you're having (at least, not obvious > for me). If, as you say, match is nil, it means that the trouble is outside > the yaml file (which is only used in the following line). So, either the data > doesn't have the expected format or the regexp isn't doing what I think it > should. Yet, trying in irb, the regexp matched the line you posted. I'm at a > loss, here. The best suggestion I can give you is to try putting a > p lines[i-1] > before the match line and see if this gives some insight on what it's > happening. > > Stefano First of all thanks for all your help so far. I just don't get it.. the output looks like this: "CATEG: CITY=0;STATE=0;\n" nil "CATEG: CITY=0;STATE=0;\n" nil and so on for all files. CATEG being another line inside the text files... and the problem might be because the Name-line isn't always above the Link-line. I could have n lines between or below, what I'm trying to say is that I don't know where the Name-line is inside the texts files.