MrDuk
MrDuk

Reputation: 18342

Ruby: How do you search for a substring, and increment a value within it?

I am trying to change a file by finding this string:

<aspect name=\"lineNumber\"><![CDATA[{CLONEINCR}]]>

and replacing {CLONEINCR} with an incrementing number. Here's what I have so far:

file = File.open('input3400.txt' , 'rb')
contents = file.read.lines.to_a
contents.each_index do |i|contents.join["<aspect name=\"lineNumber\"><![CDATA[{CLONEINCR}]]></aspect>"] = "<aspect name=\"lineNumber\"><![CDATA[#{i}]]></aspect>" end
file.close

But this seems to go on forever - do I have an infinite loop somewhere?

Note: my text file is 533,952 lines long.

Upvotes: 0

Views: 180

Answers (2)

Sean Mackesey
Sean Mackesey

Reputation: 10959

If what you want is to replace CLONEINCR with the line number, which is what your above code looks like it's trying to do, then this will work. Otherwise see Borodin's answer.

output = File.readlines('input3400.txt').map.with_index do |line, i|
  line.gsub "<aspect name=\"lineNumber\"><![CDATA[{CLONEINCR}]]></aspect>",
    "<aspect name=\"lineNumber\"><![CDATA[#{i}]]></aspect>"
end
File.write('input3400.txt', output.join(''))

Also, you should be aware that when you read the lines into contents, you are creating a String distinct from the file. You can't operate on the file directly. Instead you have to create a new String that contains what you want and then overwrite the original file.

Upvotes: 0

Borodin
Borodin

Reputation: 126772

You are repeatedly concatenating all the elements of contents, making a substitution, and throwing away the result. This is happening once for each line, so no wonder it is taking a long time.

The easiest solution would be to read the entire file into a single string and use gsub on that to modify the contents. In your example you are inserting the (zero-based) file line numbers into the CDATA. I suspect this is a mistake.

This code replaces all occurrences of <![CDATA[{CLONEINCR}]]> with <![CDATA[1]]>, <![CDATA[2]]> etc. with the number incrementing for each matching CDATA found. The modified file is sent to STDOUT. Hopefully that is what you need.

File.open('input3400.txt' , 'r') do |f|
  i = 0
  contents = f.read.gsub('<![CDATA[{CLONEINCR}]]>') { |m|
    m.sub('{CLONEINCR}', (i += 1).to_s)
  }
  puts contents
end

Upvotes: 1

Related Questions