Ruby IO.readlines stops before the last line of a file - ruby

I'm using Ruby 1.9.3 on a Windows machine.
When I run IO.readlines on a file, I get an odd and unexpected result. The result of running IO.readlines on a file is an array that just ends in a line that isn't the last line in the file. For example, the last element in the array is this string:
MLOGIC(RESPONSE_FREQUENCY): Parameter NUMERATOR_SUBSET has value \x01where
This is the last element in the array even though the file actually has many more lines.
When I run IO.readlines on the exact same file on OS X using Ruby 2.0.0, there is no issue; it reads the whole file into an array as expected.
Any assistance with this would be greatly appreciated.

The main difference of IO.readlines (docs 1.9.3 and 2.0.0) on Windows vs. OSX is the default line seperator $/—on unixoid system you'll find that to be "\n" and on Windows it is "\r\n".
To get the same behaviour, you can call IO.readlines like so:
arr = IO.readlines('./testfile', "\n")

Related

Is special variable $. gone from Ruby?

When processing a file, I used to use the special variable $. to get the last line number being read. For instance, the following program
require 'csv'
IFS=';'
CSV_OPTIONS = { col_sep: IFS, external_encoding: Encoding::ISO_8859_1, internal_encoding: Encoding::UTF_8 }
CSV.new($stdin, CSV_OPTIONS).each do |row|
puts "::::line #{$.} row=#{row}"
end
is supposed to dump a CSV file (where the fields are delimited by semicolon instead of comma, as is the case in our project) and prepend each output line by the line number.
After updating Ruby to
_ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-cygwin]_
the lines are still dumped, but the line number is always displayed as zero.
What strikes me, is that this Ruby Wiki on special Ruby variables, while still having $. in its list, doesn't have a description for this variable anymore. So I wonder: Is this variable gone, or was it never supposed to work with the csv class and just worked for me by accident in the earlier versions?
I'm not sure why $. isn't working for you, but it's also not the best solution here. When it works, $. gives you the number of lines read from input, but since quoted fields in a CSV file can span multiple lines the number you get from $. won't always be the number of rows that have been read.
As mentioned above, each_with_index is a good alternative:
CSV.new($stdin, CSV_OPTIONS).each_with_index do |row, i|
puts "::::row #{i} row=#{row}"
end
Another alternative is CSV#lineno:
lineno()
The line number of the last row read from this file. Fields with nested line-end characters will not affect this count.
You would use it like this:
csv = CSV.new($stdin, CSV_OPTIONS)
csv.each do |row|
puts "::::row #{csv.lineno} row=#{row}"
end
Note that each_with_index will start counting at 0, whereas lineno starts at 1.
You can see both approaches in action on repl.it: https://repl.it/#jrunning/LoudBlushingCharactercode

why Ruby String each_char method prints an extra % at the end

I'm a Ruby newbie, I tried to print each char in a Ruby string, using
"hello world".each_char {|c| print c}
However, when I ran the .rb program, it printed out hello world%, with a % character at the end. Then I switched to irb, it worked fined without the extra % character. Can anyone tell me how this happened? Why there was a %?
the program is doing what is expected.
the % is actually the shell prompt.
guessing you do something like:
%my-script.rb
hello world%
because you don't have a new line, when the output finishes the script just takes control back and shows the prompt

Undefined method "each" in Ruby 2.0

The new Mac OS update moved the system Ruby up to 2.0, which is great, but now I'm seeing errors in a lot of my scripts that I don't know how to fix. Specifically, I had code that called for files using mdfind and then read them, like this:
files = %x{mdfind -onlyin /Users/Username/Dropbox/Tasks 'kMDItemContentModificationDate >= "$time.today(-1)"'}
files.each do |file|
Now I'm getting an error that says
undefined method `each' for #<String:0x007f83521865c8> (NoMethodError)"
It seems as if each now needs a qualifier. I tried each_line but that yielded additional errors down the line. Is there a simple replacement for this that I'm overlooking?
Ruby 1.8 used to have String#each which was doing implicit splitting.
each(separator=$/) {|substr| block } => str
Splits str using the supplied parameter as the record separator ($/ by default), passing each substring in turn to the supplied block. If a zero-length record separator is supplied, the string is split into paragraphs delimited by multiple successive newlines.
Explicit splitting should work in modern rubies, I believe.
files.split($/).each do |file|
Where $/ is newline char. You can use explicit char, since your script is not portable anyway.
files.split("\n").each do |file|
Update
or you can just use an alias of now-extinct each
files.each_line do |file|

Ruby error using sub method with closure

I am not a Ruby developer, but I'm trying to use 1 line of Ruby script to increment a 3-part version number for a Grails application that we're building with Jenkins. The version number is stored in a simple properties file called application.properties. I know there's tons of ways to do this, but my question here is specific to Ruby and why it's not working.
For testing, I reduced application.properties to a single line:
app.version=0.2.8
All I want to do is increment the last number. That's it. So, I found some code online and hacked it a bit:
ruby -pi.bak -e 'sub(/^app\.version=(\d+)\.(\d+)\.(-?\d+)/) { "app.version=#{$1}.#{$2}.#{$3.to_i.next}" }' application.properties
This code works perfectly on Ruby 1.8.7 on OS X, but fails on Ruby 1.9.3 on Linux:
-e:1:in `sub': wrong number of arguments (1 for 1..2) (ArgumentError)
from -e:1:in `sub'
from -e:1:in `<main>'
Note that the expression works on Linux if applied directly to a string literal:
ruby -e 'puts "app.version=0.2.8".sub(/^app\.version=(\d+)\.(\d+)\.(-?\d+)/) { "app.version=#{$1}.#{$2}.#{$3.to_i.next}" }'
I also tried gsub but that didn't work either (but for a different reason).
I must be missing something simple here. I've tried other examples using a closure with sub() and it seemed to work fine.
Since BernardK suggested that not all versions of Ruby treat -p the same, and since my code worked when applied to a string literal, then here is a solution that works on both 1.8.7 and 1.9.3. Create a file called incAppVersion.rb:
if $_ =~ /^\s*app\.version\s*=/
print $_.sub(/(\d+)\.(\d+)\.(-?\d+)/) { "#{$1}.#{$2}.#{$3.to_i.next}" }
else
print $_
end
and run it with:
ruby -ni.bak incAppVer.rb application.properties
-n is cool!
Please feel free to offer suggestions for improvement.
$ ruby -pi.bak -e'$_.chomp!; $_ = $_[0..-2] + $_[-1, 1].succ + "\n" if $_[0..11] == "app.version="' application.properties
As the Pickaxe(*) explains for -p : Places your program code within the loop while gets; ...; print; end.
gets puts the line just read into $_.
chomp! removes CRLF.
print without arguments prints $_, thus we must modify $_ : $_ = ...
$_[0..-2] is the input line except the last character
$_[-1, 1] is the last character, for a length one (needed in Ruby 1.8 to get a char, not a number)
$_[-1, 1].succ is the next character/number
+ "\n" to put CRLF again (change it to "\r\n" on Windows)
if $_[0..11] == "app.version=" to process only the desired property
$ ruby -v
ruby 1.9.2p320 (2012-04-20 revision 35421) [x86_64-darwin12.2.0]
Tested on OS X ML.
File before :
app.version=0.2.8
line2
File after two executions :
app.version=0.2.10
line2
(*) http://pragprog.com/book/ruby3/programming-ruby-1-9

Write to file and keep the same formatting as the terminal does with Ruby

I am working with a file that has numbers and letters in it. I managed to remove the letters with regexps, but when I try to output the file with just the numbers in using:
matchIt = File.open('numbers.txt', 'a') {|f| f.write(string[i]) }
they appear attached together like this:
1613714531731413747
When I run a ruby script from the terminal, I normally type:
ruby script.rb > numbers.txt
which would then format the numbers downward:
1
2
3
4
5
Can I do the same thing with ruby using the command I have above?
If you're looking to print a new line for each number, just use f.puts instead of f.write.

Resources