Printing lines from a file in ruby - ruby

When I start printing lines from a file, I get this error
#<File:0x007ff65ee297b0>
Here is the code
require 'rubygems'
File.open("sample.txt", 'r') do |f|
puts f
end

You are printing the file object. To get the contents line by line, you can use File.foreach
File.foreach('sample.txt', 'r') do |line|
puts line # called for every line
end
To process the whole file at once, you can use the read method on the file object:
File.open('sample.txt', 'r') do |file|
puts file.read # called only once
end

This is not an error. It prints correctly one line which is your File object.
Here your create a file object and you did not ask it to fetch lines or anything else for that matter.
Several good answers already. But here is another way to do it with minimal change to your code:
File.open("sample.txt", 'r').each_line do |f|
puts f
end

Another way :
IO.foreach("sample.txt") {|line| line }
Or
File.foreach('sample.txt') {|line| line }

File::open returns file handle (which apparently is being printed out as #<File:0x007ff65ee297b0>.) If you need the file content line by line you might want to use IO::readlines:
IO.readlines("sample.txt").each do |line|
puts line
end

Related

Ruby: overwrite line in file

I am trying to log some state to a file using standard file i/o in ruby. The file will just have one line with a number in it. I want to read in the line and then on each iteration of a loop I want to update this number.
I know I can read in the file with
file = File.open('out.log', 'a+')
num = file.readline
The problem is, I don't know how to then overwrite the first line in a loop without just re-opening the file every iteration i.e.
file = File.open('out.log', 'w')
which will create an empty file
No need to open it each time:
file = File.open('out.log', File::RDWR)
10.times do |i|
file.seek(0) # rewind to the beginning of the file (line in your case)
file.write("iteration #{i}") # write what you want
# the following is just in order to show what was written
file.seek(0)
puts file.readline
end
file.close
You can use IO::open with block to close the file, when block exits automatically.
File.open("#{__dir__}/test.txt", File::RDWR) do |file|
10.times do |i|
file.rewind
file.puts("iteration #{i}")
end
end
puts File.read("#{__dir__}/test.txt")
# >> iteration 9

How to add new line in a file

I want to add newline character below.
But the result is wrong.
Teach me what is wrong.
test.txt(before)
------------------
2014-09
2014-10
2014-11
------------------
test.txt(after)
------------------
2014-09
2014-10
2014-11
------------------
I make a ruby script below, but the result is wrong.
f = File.open("test.txt","r+")
f.each{|line|
if line.include?("2014-10")
f.puts nil
end
}
f.close
the result
------------------
2014-09
2014-10
014-11
------------------
To solve your problem, the easiest way is to create a new file to output your new text into. To do you'll need to open the input file and the output file and iterate each line of the file check the condition and put desired line into the output file.
Example
require 'fileutils'
File.open("text-output.txt", "w") do |output|
File.foreach("text.txt") do |line|
if line.include?("2014-10")
output.puts line + "\n"
else
output.puts line
end
end
end
FileUtils.mv("text-output.txt", "text.txt")
Easy way
File.write(f = "text.txt", File.read(f).gsub(/2014-10/,"2014-10\n"))
Reading and writing a file at the same time can get messy, same thing with other data structures like arrays. You should build a new file as you go along.
Some notes:
you should use the block form of File.open because it will stop you from forgetting to call f.close
puts nil is the same as puts without arguments
single quotes are preferred over double quotes when you don’t need string interpolation
you should use do ... end instead of { ... } for multi-line blocks
File.open(...).each can be replaced with File.foreach
the intermediate result can be stored in a StringIO object which will respond to puts etc.
Example:
require 'stringio'
file = 'test.txt'
output = StringIO.new
File.foreach(file) do |line|
if line.include? '2014-10'
output.puts
else
output << line
end
end
output.rewind
File.open(file, 'w') do |f|
f.write output.read
end

Line replacement in directories clears the whole files?

I am trying to recursively replace a whole line from index.html files into a directory with sub-directories.
The code above puts the right lines I'm searching with the var "pattern", but when I run it, it removes everything form my index.html files.
pattern = "Keyword"
replacement = "<td width=\"30\"><img src=\"styles/img/trans.gif\" width=\"30\"></td>"
Dir.glob('/Users/root/Desktop/directory/test/**/index.html') do |item|
next unless File.file?(item)
File.open(item, "w+:ASCII-8BIT") do |f|
f.each_line do |line|
if line.match(pattern)
my_line = line
line.sub(my_line, replacement)
end
end
end
end
What am I doing wrong ?
You need to read the file first, build the expected output, and then write it:
Dir.glob('/Users/root/Desktop/directory/test/**/index.html') do |item|
next unless File.file?(item)
output = IO.readlines(item).map do |line|
if line.match(pattern)
replacement
else
line
end
end
File.open(item, "w+:ASCII-8BIT") do |f|
f.write output.join
end
end
end
You use File.open with open mode w+ which, according to Ruby documentation, is:
"w+" Read-write, truncates existing file to zero length or creates a new file for reading and writing.
To read the file and put some lines use r:
File.open(item, "r:ASCII-8BIT")

Read Certain Lines from File

Hi just getting into Ruby, and I am trying to learn some basic file reading commands, and I haven't found any solid sources yet.
I am trying to go through certain lines from that file, til the end of the file.
So in the file where it says FILE_SOURCES I want to read all the sources til end of file, and place them in a file.
I found printing the whole file, and replacing words in the file, but I just want to read certain parts in the file.
Usually you follow a pattern like this if you're trying to extract a section from a file that's delimited somehow:
open(filename) do |f|
state = nil
while (line = f.gets)
case (state)
when nil
# Look for the line beginning with "FILE_SOURCES"
if (line.match(/^FILE_SOURCES/))
state = :sources
end
when :sources
# Stop printing if you hit something starting with "END"
if (line.match(/^END/))
state = nil
else
print line
end
end
end
end
You can change from one state to another depending on what part of the file you're in.
I would do it like this (assuming you can read the entire file into memory):
source_lines = IO.readlines('source_file.txt')
start_line = source_lines.index{ |line| line =~ /SOURCE_LINE/ } + 1
File.open( 'other_file.txt', 'w' ) do |f|
f << source_lines[ start_line..-1 ].join( "\n" )
end
Relevant methods:
IO.readlines to read the lines into an array
Array#index to find the index of the first line matching a regular expression
File.open to create a new file on disk (and automatically close it when done)
Array#[] to get the subset of lines from the index to the end
If you can't read the entire file into memory, then I'd do a simpler variation on #tadman's state-based one:
started = false
File.open( 'other_file.txt', 'w' ) do |output|
IO.foreach( 'source_file.txt' ) do |line|
if started then
output << line
elsif line =~ /FILE_SOURCES/
started = true
end
end
end
Welcome to Ruby!
File.open("file_to_read.txt", "r") {|f|
line = f.gets
until line.include?("FILE_SOURCES")
line = f.gets
end
File.open("file_to_write.txt", "w") {|new_file|
f.each_line {|line|
new_file.puts(line)
}
new_file.close
}
f.close
}
IO functions have no idea what "lines" in a file are. There's no straightforward way to skip to a certain line in a file, you'll have to read it all and ignore the lines you don't need.

Ruby: How to replace text in a file?

The following code is a line in an xml file:
<appId>455360226</appId>
How can I replace the number between the 2 tags with another number using ruby?
There is no possibility to modify a file content in one step (at least none I know, when the file size would change).
You have to read the file and store the modified text in another file.
replace="100"
infile = "xmlfile_in"
outfile = "xmlfile_out"
File.open(outfile, 'w') do |out|
out << File.open(infile).read.gsub(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
end
Or you read the file content to memory and afterwords you overwrite the file with the modified content:
replace="100"
filename = "xmlfile_in"
outdata = File.read(filename).gsub(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
File.open(filename, 'w') do |out|
out << outdata
end
(Hope it works, the code is not tested)
You can do it in one line like this:
IO.write(filepath, File.open(filepath) {|f| f.read.gsub(//<appId>\d+<\/appId>/, "<appId>42</appId>"/)})
IO.write truncates the given file by default, so if you read the text first, perform the regex String.gsub and return the resulting string using File.open in block mode, it will replace the file's content in one fell swoop.
I like the way this reads, but it can be written in multiple lines too of course:
IO.write(filepath, File.open(filepath) do |f|
f.read.gsub(//<appId>\d+<\/appId>/, "<appId>42</appId>"/)
end
)
replace="100"
File.open("xmlfile").each do |line|
if line[/<appId>/ ]
line.sub!(/<appId>\d+<\/appId>/, "<appId>#{replace}</appId>")
end
puts line
end
The right way is to use an XML parsing tool, and example of which is XmlSimple.
You did tag your question with regex. If you really must do it with a regex then
s = "Blah blah <appId>455360226</appId> blah"
s.sub(/<appId>\d+<\/appId>/, "<appId>42</appId>")
is an illustration of the kind of thing you can do but shouldn't.

Resources