Read file and write file - ruby

Can't seem to get my data to be read as an integer and print out the data. plus there is a close stream (IOError) for count = aFile.gets in def read (afile) function.
This program includes Array, files and loops. The purpose of this program is to take a number 10 and write the number to a file then on each line increment from zero to 10 that is passed.
# takes a number and writes that number to a file then on each line
# increments from zero to the number passed
def write(aFile, number)
# You might need to fix this next line:
aFile.puts("number")
index = 0
while (index < number)
aFile.puts(number.to_s)
index += 1
end
end
# Read the data from the file and print out each line
def read(aFile)
# Defensive programming:
count = aFile.gets
if (is_numeric(count))
count = count.to_i
else
count = 0
puts "Error: first line of file is not a number"
end
index = 0
while (count < index)
line = aFile.gets
puts "Line read: " + line
end
end
# Write data to a file then read it in and print it out
def main
aFile = File.new("mydata.txt", "w") # open for writing
if aFile # if nil this test will be false
write(aFile, 10)
else
puts "Unable to open file to write!"
end
if aFile
read(aFile)
end
aFile.close
end
# returns true if a string contains only digits
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
main

If you want to make your code work, change:
aFile = File.new("mydata.txt", "w")
to:
aFile = File.new("mydata.txt", "r+")
You can change:
count = aFile.gets
if (is_numeric(count))
to:
count = aFile.gets.to_i
if (count.is_a?(Fixnum))
and then get rid of the is_numeric?(obj) method.
Also you're not incrementing the counter, you can fix that as well.

Here is a skinned code working, you'd easily add the features I removed.
def write(a_file, number)
(1..number).each { |n| a_file.puts(n) }
end
def read(a_file)
a_file.each { |line| puts line }
end
def main
a_file = File.new("mydata.txt", "w")
if a_file
write(a_file, 10)
else
puts "Unable to open file to write!"
end
a_file.close
a_file = File.open("mydata.txt", "r")
if a_file
read(a_file)
end
a_file.close
end
main
The main bugs I've found:
After writing, close the file and open again fro writing
See here for reading: What are all the common ways to read a file in Ruby?
aFile.puts(number.to_s) you should puts index, which is the incrementing variable
(is_numeric(count)) missing ?
Side note: use Ruby notation for variables: a_file is good, aFile is not.

Related

Why am I getting "not a number" error in this code? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
I'm just hoping someone might be able to help me out with this code:
def write(aFile, number)
index = 1
while (index < number)
aFile.puts(index.to_s)
index += 1
end
end
def read(aFile)
count = aFile.gets
if (is_numeric?(count))
count = count.to_i
else
count = 0
puts "Error: first line of file is not a number"
end
index = 0
while (count < index)
line = aFile.gets
puts "Line read: " + line
end
end
# Write data to a file then read it in and print it out
def main
aFile = File.new("mydata.txt", "w")
if aFile
write(aFile, 11)
aFile.close
else
puts "Unable to open file to write!"
end
aFile = File.new("mydata.txt", "r")
if aFile
read(aFile)
aFile.close
else
puts "Unable to open file to read!"
end
end
# returns true if a string contains only digits
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
main
The result I'm trying to get is this:
Line read: 0
Line read: 1
...
Line read: 10
But I'm getting:
Error: first line of file is not a number
Why is this the case? Something must be wrong with my code.
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
Result of a code block (such as method body) is the last expression evaluated in it. Your true becomes the value of the if and is ignored, because the next expression evaluated is false, which is what is always returned. There are several ways you can improve this.
def is_numeric?(obj)
return true if /[^0-9]/.match(obj).nil?
false
end
def is_numeric?(obj)
/[^0-9]/.match(obj).nil?
end
def is_numeric?(obj)
/[^0-9]/ !~ obj
end
def is_numeric?(obj)
Integer(obj) rescue false
end
And many more.

Read and Write Ruby

Can't seem to get my data to be read as an integer and print out the data, instead gets the 2nd option which is Error: first line of file is not a number.
def write(aFile, number)
aFile.puts(number)
index = 0
while (index < number)
aFile.puts(index)
index += 1
end
end
def read(aFile)
count = aFile.gets
if (is_numeric?(count))
count = count.to_i
else
count = 0
puts "Error: first line of file is not a number"
end
index = 0
while (count < index)
line = aFile.gets
puts "Line read: " + line
index += 1
end
end
def main
aFile = File.new("mydata.txt", "w") # open for writing
if aFile # if nil this test will be false
write(aFile, 10)
aFile.close
aFile = File.new("mydata.txt", "r")
read(aFile)
aFile.close
else
puts "Unable to open file to write or read!"
end
end
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
main
Any help on how to fix this would be great.
Your problem is a lack of return
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
true
end
false
end
This function ALWAYS returns false. There is no case where it ever returns true. Ruby functions always return at an explicit return() and if none is called, then the last line is returned. That means the true you have there does nothing. It's simply thrown away and false is returned.
A simplified form of this existing function is just:
def is_numeric?(obj)
false
end
To fix this problem, you need to return when it’s true:
def is_numeric?(obj)
if /[^0-9]/.match(obj) == nil
return(true)
end
false
end
You can also simplify this to:
def is_numeric?(obj)
/[^0-9]/.match(obj).nil?
end
Also, if you’re using Ruby 2.4+, a more efficient way to do this would be to use the match? method and a negation. match sets up some handy MatchData (and backreferences), but since you if you don't need any of that, you can save the overhead by using match?, which simply returns a boolean.
def is_numeric?(obj)
!/[^0-9]/.match?(obj)
end
Another problem is your logic of count < index.
while (count < index)
line = aFile.gets
puts "Line read: " + line
index += 1
end
Since index is 0, the only time count will be less than index, is if count is less than 0. Perhaps you meant while (count > index)?
Notes:
https://www.ruby-lang.org/en/news/2016/12/25/ruby-2-4-0-released/

File comparison Ran out of memory

Hi i am using this code - but for files > 8 Million rows of lines - 2 files passed as text input, the memory runs out. How can i compare both text files which are more than 30 Million lines maybe.
fileA1 = ARGV[0]
fileA2 = ARGV[1]
if ARGV.length != 2
raise 'Send Two files pls'
end
cmd = "sort #{fileA1} > Sorted.txt"
`#{cmd}`
aFile = File.open("Sorted.txt", "r");
bFile = File.open(fileA2, "r").readlines;
fileR = File.open("result.txt", "w")
p aFile.class
p bFile.class
p bFile.length
aFile.each do |e|
if(! bFile.include?(e) )
p 'Able to get differences:' + e.to_s
fileR.write('Does not Include:' + e)
end
end
additional coding i tried without luck.
counterA = counterB = 0
aFile = File.open("Sample1 - Copy.txt", "r");
bFile = File.open("Sample2.txt", "r");
file1lines = aFile.readlines
file2lines = bFile.readlines
file1lines.each do |e|
if(!file2lines.include?(e))
puts e
else
p "Files include these lines:"
end
end
stopTime = Time.now
As a starting point, I would use the diff Unix command (available on Windows as part of Cygwin, etc), and see if that addresses your need:
#!/usr/bin/env ruby
raise "Syntax is comp_files file1 file2" unless ARGV.length == 2
file1, file2 = ARGV
`sort #{file1} > file1_sorted.txt`
`sort #{file2} > file2_sorted.txt`
`diff file1_sorted.txt file2_sorted.txt 2>&1 > diff.txt`
puts 'Created diff.txt.' # After running the script, view it w/less, etc.
Here is a similar script that uses temporary files that are automatically deleted before exiting:
#!/usr/bin/env ruby
raise "Syntax is comp_files file1 file2" unless ARGV.length == 2
require 'tempfile'
input_file1, input_file2 = ARGV
sorted_file1 = Tempfile.new('comp_files_sorted_1').path
sorted_file2 = Tempfile.new('comp_files_sorted_2').path
puts [sorted_file1, sorted_file2]
`sort #{input_file1} > #{sorted_file1}`
`sort #{input_file2} > #{sorted_file2}`
`diff #{sorted_file1} #{sorted_file2} 2>&1 > diff.txt`
puts 'Created diff.txt.' # After running the script, view it w/less, etc.
# The code below can be used to create sample input files
# File.write('input1.txt', (('a'..'j').to_a.shuffle + %w(s y)).join("\n"))
# File.write('input2.txt', (('a'..'j').to_a.shuffle + %w(s t z)).join("\n"))
I believe your problem is with readlines. This method will read the entire file and return a string. Since your file is huge, you will risk running out of memory.
To work with large files, don't read the entire contents at once, but read in pieces as needed.
Also, your algorithm has another problem since the comparison really checks whether all lines in aFile are included in bFile without actually checking for order at all. I'm not sure if that is indeed your intent.
If you really want to compare line by line and if order matters, then your comparison should be line-by-line and you don't have to read the entire file into a string. Use the gets method instead, which by default returns the next line in a file or nil at EOF.
Something like this:
aFile.each do |e|
if e != bFile.gets
p 'Able to get differences:' + e.to_s
fileR.write('Does not Include:' + e)
end
end
On the other hand, if you really want to find if all lines in a are in b, regardless of order, you can do a nested loop, where for each line in a, you iterate all lines of b. Make sure to return on first match to speedy things up since this will be a really expensive operation, but the include call is also expensive so it's probably a tie IMO with the exception of the file IO overhead.
Here is a script that will analyze 2 text files, reporting the first difference, or a difference in the number of lines, or success.
NOTE: THE CODE HERE IS TRUNCATED. PLEASE GO TO https://gist.github.com/keithrbennett/1d043fdf7b685d9692f0181ad68c6307 FOR THE COMPLETE SCRIPT!
#!/usr/bin/env ruby
raise "Syntax is first_diff file1 file2" unless ARGV.size == 2
FILE1, FILE2 = ARGV
ENUM1 = File.new(FILE1).to_enum
ENUM2 = File.new(FILE2).to_enum
def build_unequal_error_message(line_num, line1, line2)
"Difference found at line #{line_num}:
#{FILE1}: #{line1}
#{FILE2}: #{line2}"
end
def build_unequal_line_count_error_message(line_count, file_exhausted)
"All lines up to line #{line_count} were identical, " \
"but #{file_exhausted} has no more text lines."
end
def get_line(file_enumerator)
file_enumerator.next.chomp
end
def has_next(enumerator)
begin
enumerator.peek
true
rescue StopIteration
false
end
end
# Returns an analysis of the results in the form of a string
# if a compare error occurred, else returns nil.
def error_text_or_nil
line_num = 0
loop do
has1 = has_next(ENUM1)
has2 = has_next(ENUM2)
case
when has1 && has2
line1 = get_line(ENUM1)
line2 = get_line(ENUM2)
if line1 != line2
return build_unequal_error_message(line_num, line1, line2)
end
when !has1 && !has2
return nil # if both have no more values, we're done
else # only 1 enum has been exhausted
exhausted_file = has1 ? FILE2 : FILE1
not_exhausted_file = exhausted_file == FILE1 ? FILE2 : FILE1
return build_unequal_line_count_error_message(line_num, exhausted_file)
end
line_num += 1
end
puts "Lines processed successfully: #{line_num}"
end
result = error_text_or_nil
if result
puts result
exit -1
else
puts "Compare successful"
exit 0
end

How to write a multidimensional array into separate files and then read from them in order in Ruby

I want to take a file, read the file into my program and split it into characters, split the resulting character array into a multidimensional array of 5,000 characters each, then write each separate array into a file found in the same location.
I have taken a file, read it, and created the multidimensional array. Now I want to write each separate single dimension array into separate files.
The file is obtained via user input. Then I created a chain helper method that stores the file to an array in the first mixin, this is then passed to another method that breaks it down into a multidimensional array, which finally hands it off to the end of the chain which currently is setup to make a new directory for which I will put these files.
require 'Benchmark/ips'
file = "C:\\test.php"
class String
def file_to_array
file = self
return_file = File.open(file) do |line|
line.each_char.to_a
end
return return_file
end
def file_write
file_to_write = self
if Dir.exist?("I:\\file_to_array")
File.open("I:/file_to_array/tmp.txt", "w") { |file| file.write(file_to_write) }
read_file = File.read("I:/file_to_array/tmp.txt")
else
Dir.mkdir("I:\\file_to_array")
end
end
end
class Array
def file_divider
file_to_divide = self
file_to_separate = []
count = 0
while count != file_to_divide.length
separator = count % 5000
if separator == 0
start = count - 5000
stop = count
file_to_separate << file_to_divide[start..stop]
end
count = count + 1
end
return file_to_separate
end
def file_write
file_to_write = self
if Dir.exist?("I:\\file_to_array")
File.open("I:/file_to_array/tmp.txt", "w") { |file| file.write(file_to_write) }
else
Dir.mkdir("I:\\file_to_array")
end
end
end
Benchmark.ips do |result|
result.report { file.file_to_array.file_divider.file_write }
end
Test.php
<?php
echo "hello world"
?>
This untested code is where I'd start to split text into chunks and save it:
str = "I want to take a file"
str_array = str.scan(/.{1,10}/) # => ["I want to ", "take a fil", "e"]
str_array.each.with_index(1) do |str_chunk, i|
File.write("output#{i}", str_chunk)
end
This doesn't honor word-boundaries.
Reading a separate input file is easy; You can use read if you KNOW the input will never exceed the available memory and you don't care about performance.
Thinking about it further, if you want to read a text file and break its contents into smaller files, then read it in chunks:
input = File.open('input.txt', 'r')
i = 1
until input.eof? do
chunk = input.read(10)
File.write("output#{i}", chunk)
i += 1
end
input.close
Or even better because it automatically closes the input:
File.open('input.txt', 'r') do |input|
i = 1
until input.eof? do
chunk = File.read(10)
File.write("output#{i}", chunk)
i += 1
end
end
Those are not tested but it look about right.
Use standard File API and Serialisation.
File.write('path/to/yourfile.txt', Marshal.dump([1, 2, 3]))

How do I detect end of file in Ruby?

I wrote the following script to read a CSV file:
f = File.open("aFile.csv")
text = f.read
text.each_line do |line|
if (f.eof?)
puts "End of file reached"
else
line_num +=1
if(line_num < 6) then
puts "____SKIPPED LINE____"
next
end
end
arr = line.split(",")
puts "line number = #{line_num}"
end
This code runs fine if I take out the line:
if (f.eof?)
puts "End of file reached"
With this line in I get an exception.
I was wondering how I can detect the end of file in the code above.
Try this short example:
f = File.open(__FILE__)
text = f.read
p f.eof? # -> true
p text.class #-> String
With f.read you read the whole file into text and reach EOF.
(Remark: __FILE__ is the script file itself. You may use you csv-file).
In your code you use text.each_line. This executes each_line for the string text. It has no effect on f.
You could use File#each_line without using a variable text. The test for EOF is not necessary. each_line loops on each line and detects EOF on its own.
f = File.open(__FILE__)
line_num = 0
f.each_line do |line|
line_num +=1
if (line_num < 6)
puts "____SKIPPED LINE____"
next
end
arr = line.split(",")
puts "line number = #{line_num}"
end
f.close
You should close the file after reading it. To use blocks for this is more Ruby-like:
line_num = 0
File.open(__FILE__) do | f|
f.each_line do |line|
line_num +=1
if (line_num < 6)
puts "____SKIPPED LINE____"
next
end
arr = line.split(",")
puts "line number = #{line_num}"
end
end
One general remark: There is a CSV library in Ruby. Normally it is better to use that.
https://www.ruby-forum.com/topic/218093#946117 talks about this.
content = File.read("file.txt")
content = File.readlines("file.txt")
The above 'slurps' the entire file into memory.
File.foreach("file.txt") {|line| content << line}
You can also use IO#each_line. These last two options do not read the entire file into memory. The use of the block makes this automatically close your IO object as well. There are other ways as well, IO and File classes are pretty feature rich!
I refer to IO objects, as File is a subclass of IO. I tend to use IO when I don't really need the added methods from File class for the object.
In this way you don't need to deal with EOF, Ruby will for you.
Sometimes the best handling is not to, when you really don't need to.
Of course, Ruby has a method for this.
Without testing this, it seems you should perform a rescue rather than checking.
http://www.ruby-doc.org/core-2.0/EOFError.html
file = File.open("aFile.csv")
begin
loop do
some_line = file.readline
# some stuff
end
rescue EOFError
# You've reached the end. Handle it.
end

Resources