I need to find each occurrence of "$" and change it to a number using a count. eg str = "foo $ bar $ foo $ bar $ * run code here * => "foo 1 bar 2 foo 3 bar 4
It feels like this should be a lot easier than i'm making it out to be. Here's my code:
def counter(file)
f = File.open(file, "r+")
count = 0
contents = f.readlines do |s|
if s.scan =~ /\$/
count += 1
f.seek(1)
s.sub(/\$/, count.to_s)
else
puts "Total changes: #{count}"
end
end
end
However I'm not sure if I'm meant to be using .match, .scan, .find or whatever else.
When i run this it doesn't come up with any errors but it doesn't change anything either.
Your syntax for scan is incorrect and it should throw error.
You can try something along this line:
count = 0
str = "foo $ bar $ foo $ bar $ "
occurences = str.scan('$')
# => ["$", "$", "$", "$"]
occurences.size.times do str.sub!('$', (count+=1).to_s) end
str
# => "foo 1 bar 2 foo 3 bar 4 "
Explanation:
I am finding all occurences of $ in the string, then I am using sub! in iteration as it replaces only the first occurrence at a time.
Note: You may want to improve scan line by using regex with boundary match instead of plain "$" as it will replace $ even from within words. Eg: exa$mple will also get replace to something like: exa1mple
Why your code is not throwing error?
If you read the description about readlines, you will find:
Reads the entire file specified by name as individual lines, and
returns those lines in an array.
As it reads the entire file at once there is no value passing block along this method. Following example will make it more clear:
contents = f.readlines do |s|
puts "HELLO"
end
# => ["a\n", "b\n", "c\n", "d\n", "asdasd\n", "\n"] #lines of file f
As you can see "HELLO" never gets printed, showing the block code is never executed.
Related
data = File.read("data.txt")
if(data.to_s.eql? "hello")
...
end
My data.txt is filled with "hello" as well, so the if-loop should get active but it doesn't. What do I do wrong?
When reading data from a file, it's likely you get a newline tagged on to the end of your data.
For example, if I run the following in the terminal to create a file containing only the word 'hello':
echo hello > data.txt
And the read this in the terminal, I see:
cat data.txt
# => hello
However, jumping into irb, I get the following:
File.read("data.txt")
# => "hello\n"
The \n is the newline operator.
To solve your question, you can use:
if data.chomp == "hello"
...
end
chomp removes any record separator from the end of the string, giving you the comparison you're after.
If you just want to know whether the file contains the specified string, you can also use:
data['hello']
This will return the truthy value in the square brackets if present, or nil if not.
if data["hello"]
...
end
I'm trying to go through a string:
str = "foo chapter 1 bar v1 baz v2 qux chapter 2 quux v1"
and find chapter numbers and verse numbers, e.g. ("chapter 1 foo v1"). When I find a verse number, I want to add the text:
"id=\"(current chapter number)_(current verse number)\""
My expected output is:
"foo chapter 1 bar id=\"chapter_1_v1\" baz id=\"chapter_1_v2\" qux chapter 2 quux id=\"chapter_2_v1\""
Using split removes whatever text that doesn't have the specified text to split on. This is my code:
str.split(/(?=chapter \d+)/).each do |c|
c.scan(/(chapter) (\d+)/) {|chap, num| puts c.gsub(/(v\d+)/, 'id="' + chap.to_s + '_' + num.to_s + '_\1"')}
end
How do I keep the text before the split? or what is a better way of achieving this result?
Instead of splitting, you could use gsub!() to directly replace the text for every match. And there's a catch, if it matches chapter \d+, simply store the value and don't do a replace (replace with the whole match).
I'll use the following regex to either match a chapter or a verse:
/\bchapter (\d+)|\b(v\d+)\b/
Code:
c = "foo chapter 1 bar v1 baz v2 qux chapter 2 quux v1"
current_chapter = "1"
c.gsub!(/\bchapter (\d+)|\b(v\d+)\b/) { |match|
if ($1)
current_chapter = $1
match
else
"id=\"chapter_" + current_chapter + "_#$2\""
end
}
puts c
Output:
foo chapter 1 bar id="chapter_1_v1" baz id="chapter_1_v2" qux chapter 2 quux id="chapter_2_v1"
DEMO
Disclaimer: I never code in Ruby, so please consider the logic I used, knowing the script more than probably should be improved. -All edits are more than welcome!
I read file using ruby and use .split to split line.
Example.txt
1
2
3
line1,line2,line3= #line.to_s.split("\n",3)
#actual
line1 => ["1
line2 => ", "2
line3 => ", "3"]
#what I expect
line1=1
line2=2
line3=3
how can i get what i expected?
Edit: it 's just 1 new line because I can't enter 1 new line in my question. To be more specific:
Example.txt
first_line\nsecond_line\nthird_line
File.open('Example.txt', 'r') do |f1|
#line = f1.readlines
f1.close
end
line1,line2,line3= #line.to_s.split("\n",3)
#actual
line1 => ["first_line
line2 => ", "second_line
line3 => ", "third_line"]
#what I expect
line1=first_line
line2=second_line
line3=third_line
You can't split using '\n', if you're trying to use line-ends. You MUST use "\n".
Strings using '\n' do not interpret \n as a line-ending, instead, they treat it as a literal backslash followed by "n":
'\n' # => "\\n"
"\n" # => "\n"
The question isn't at all clear, nor is the input file example clear given the little example code presented, however, guessing at what you want from the desired result...
If the input is a file called 'example.txt' looking like:
1
2
3
You can read it numerous ways:
File.read('example.txt').split("\n")
# => ["1", "2", "3"]
Or:
File.readlines('example.txt').map(&:chomp)
# => ["1", "2", "3"]
Either of those work, however, there is a very bad precedence set when reading files into memory like this. It's called "slurping" and can crash your code or take it, and the machine it's running on, to a crawl if the file is larger than the available memory. And, even if it fits into memory, loading a huge file into memory can cause pauses as memory is allocated, and reallocated. So, don't do that.
Instead, read the file line-by-line and process it that way if at all possible:
File.foreach('example.txt') do |line|
puts line
end
# >> 1
# >> 2
# >> 3
Don't do this:
File.open('Example.txt', 'r') do |f1|
#line = f1.readlines
f1.close
end
Ruby will automatically close a file opened like this:
File.open('Example.txt', 'r') do |f1|
...
end
There is no need to use close inside the block.
Depends a bit on what exactly you're expecting (and what #line is). If you are looking for numbers you can use:
line1, line2, line3 = #line.to_s.scan(/\d/)
If you want other characters you can use some other regular expression.
Okay, so I'm building something that takes a text file and breaks it up into multiple sections that are further divided into entries, and then puts <a> tags around part of each entry. I have an instance variable, #section_name, that I need to use in making the link. The problem is, #section_name seems to lose its value if I look at it wrong. Some code:
def find_entries
#sections.each do |section|
#entries = section.to_s.shatter(/(some) RegEx/)
#section_name = $1.to_s
puts #section_name
add_links
end
end
def add_links
puts "looking for #{#section_name} in #{#section_hash}"
section_link = #section_hash.fetch(#section_name)
end
If I comment out the call to add_links, it spits out the names of all the sections, but if I include it, I just get:
looking for in {"contents" => "of", "the" => "hash"}
Any help is much appreciated!
$1 is a global variable which can be used in later code.$n contains the n-th (...) capture of the last match
"foobar".sub(/foo(.*)/, '\1\1')
puts "The matching word was #{$1}" #=> The matching word was bar
"123 456 789" =~ /(\d\d)(\d)/
p [$1, $2] #=> ["12", "3"]
So I think #entries = section.to_s.shatter(/(some) RegEx/) line is not doing match properly. thus your first matched group contains nothing. so $1 prints nil.
In Ruby language, how can I get the number of lines in a string?
There is a lines method for strings which returns an Enumerator. Call count on the enumerator.
str = "Hello\nWorld"
str.lines.count # 2
str = "Hello\nWorld\n" # trailing newline is ignored
str.lines.count # 2
The lines method was introduced in Ruby 1.8.7. If you're using an older version, checkout the answers by #mipadi and #Greg.
One way would be to count the number of line endings (\n or \r\n, depending on the string), the caveat being that if the string does not end in a new line, you'll have to make sure to add one to your count. You could do so with the following:
c = my_string.count("\n")
c += 1 unless c[-1,1] == "\n"
You could also just loop through the string and count the lines:
c = 0
my_string.each { |line| c += 1 }
Continuing with that solution, you could get really fancy and use inject:
c = my_string.each.inject(0) { |count, line| count += 1 }
string".split("\n").size works nicely. I like that it ignores trailing new-lines if they don't contain content.
"Hello\nWorld\n".split("\n") # => ["Hello", "World"]
"hello\nworld\nfoo bar\n\n".split("\n").size # => 3
That might not be what you want, so use lines() as #Anurag suggested instead if you need to honor all new-lines.
"hello\nworld\nfoo bar\n\n".lines.count # => 4
"hello\nworld\nfoo bar\n\n".chomp.split("\n",-1).size # => 4
String#chomp gets rid of an end of line if it exists, and the -1 allows empty strings.
given a file object (here, in rails)
file = File.open(File.join(Rails.root, 'lib', 'file.json'))
file.readlines.count
returns the number of lines
IO#readlines performs a split method on strings (IOStrings in this case) using newlines as the separator
This will not count blank lines:
string.split("\n").select{ |line| line != "" }.size