I have a text file and I need to search for a string, if that string is found I want to delete entire raw containing that string and add a new raw. As for now I just try to find a string and delete entire raw. And the string I search should be given as an argument.
Code I have as far goes here:
arg1, arg2, arg3 = ARGV
read_file = File.new('conf.txt', "r").read
write_file = File.new('conf.txt', "w")
read_file.each_line do |line|
write_file.write(line) unless line.include? arg1 arg2 arg3
end
But the output I get is as follows:
C:/Ruby/tests/replace3.rb:9:in `block in <main>': undefined method `arg2' for ma
in:Object (NoMethodError)
from C:/Ruby/tests/replace3.rb:8:in `each_line'
from C:/Ruby/tests/replace3.rb:8:in `<main>'
Another approach I try is the following:
file_names = ['config.txt']
file_names.each do |file_name|
text = File.read(file_name)
new_contents = text.gsub(/regex_string/, "new_string")
File.open(file_name, "w") {|file| file.puts new_contents }
end
But the problem here is that the regex string finds only certan string and doesn't take all that raw. Maybe you have any idea how I can optimize this code for my problem.
So include? expects to receive a single argument:
line.include? single_variable
Take a look here:
http://apidock.com/ruby/Array/include%3F
EDIT:
You could try something like this:
unwanted_array = ['unwanted', 'error', 'string']
read_file.each_line do |line|
write_file.write(line) unless unwanted_array.map { |val| line.include? val }.reduce(:|)
end
Unwanted array could also be built up by doing:
unwanted_array = [arg1, arg2, arg3]
Related
E.G.
def do_the_thing(file_to_load, hash_path)
file = File.read(file)
data = JSON.parse(file, { symbolize_names: true })
data[sections.to_sym]
end
do_the_thing(file_I_want, '[:foo][:bar][0]')
Tried a few methods but failed so far.
Thanks for any help in advance :)
Assuming you missed the parameters names...
Lets assume our file is:
// test.json
{
"foo": {
"bar": ["foobar"]
}
}
Recomended solution
Does your param really need to be a string??
If your code can be more flexible, and pass arguments as they are on ruby, you can use the Hash dig method:
require 'json'
def do_the_thing(file, *hash_path)
file = File.read(file)
data = JSON.parse(file, symbolize_names: true)
data.dig(*hash_path)
end
do_the_thing('test.json', :foo, :bar, 0)
You should get
"foobar"
It should work fine !!
Read the rest of the answer if that doesn't satisfy your question
Alternative solution (using the same argument)
If you REALLY need to use that argument as string, you can;
Treat your params to adapt to the first solution, it won't be a small or fancy code, but it will work:
require 'json'
BRACKET_REGEX = /(\[[^\[]*\])/.freeze
# Treats the literal string to it's correspondent value
def treat_type(param)
# Remove the remaining brackets from the string
# You could do this step directly on the regex if you want to
param = param[1..-2]
case param[0]
# Checks if it is a string
when '\''
param[1..-2]
# Checks if it is a symbol
when ':'
param[1..-1].to_sym
else
begin
Integer(param)
rescue ArgumentError
param
end
end
end
# Converts your param to the accepted pattern of 'dig' method
def string_to_args(param)
# Scan method will break the match results of the regex into an array
param.scan(BRACKET_REGEX).flatten.map { |match| treat_type(match) }
end
def do_the_thing(file, hash_path)
hash_path = string_to_args(hash_path)
file = File.read(file)
data = JSON.parse(file, symbolize_names: true)
data.dig(*hash_path)
end
so:
do_the_thing('test.json', '[:foo][:bar][0]')
returns
"foobar"
This solution though is open to bugs when the "hash_path" is not on an acceptable pattern, and treating it's bugs might make the code even longer
Shortest solution (Not safe)
You can use Kernel eval method which I EXTREMELY discourage to use for security reasons, read the documentation and understand its danger before using it
require 'json'
def do_the_thing(file, hash_path)
file = File.read(file)
data = JSON.parse(file, symbolize_names: true)
eval("data#{hash_path}")
end
do_the_thing('test.json', '[:foo][:bar][0]')
If the procedure you were trying to work with was just extracting the JSON data to an object, you might find yourself using either of the following scenarios:
def do_the_thing(file_to_load)
file = File.read(file)
data = JSON.parse(file, { symbolize_names: true })
data[sections.to_sym]
end
do_the_thing(file_I_want)[:foo][:bar][0]
or use the dig function of Hash :
def do_the_thing(file_to_load, sections)
file = File.read(file)
data = JSON.parse(file, { symbolize_names: true })
data.dig(*sections)
end
do_the_thing(file_I_want, [:foo, :bar, 0])
I'm trying to read from a file a string that interpolates variables which are defined in the code, and substitutes the string value of those variables.
The text file:
my name is #{name}
the code:
file = File.open("tesst.txt", "r")
arr = []
name = "CDJ"
file.each_line.with_index { |line, index|
puts line
}
file.close
Desired output:
My name is CDJ
Actual output:
My name is #{name}
When output is line.inspect:
"My name is \#{name}"
Can anyone help me format this string correctly so it reads #{name} as a variable instead of a string with the inserted escape character?
file= 'tesst.txt'
name = 'CDJ'
File.readlines(file).each do |line|
eval("puts \"#{line}\"")
end
Consider this:
class Template
def initialize(source: DATA)
#text = source.read.chomp.split("\n")
end
def render(locals: {})
locals.each do |key, value|
instance_variable_set("##{key}", value)
self.class.send(:attr_reader, key)
end
#text
.map { |line| eval("\"#{line}\"") }
.join("\n")
end
end
puts Template
.new(source: File.open('./tesst.txt', 'r'))
.render(locals: {name: 'George', age: 29})
__END__
my name is #{name}
I am #{age} years old
Notes:
Variables can be provided through the hash.
File can be switched with DATA to read contents from bottom of file after __END__
I've got a list of devices:
ipc-bei640-r-br-01
ipc-bei640-r-br-02
ipc-bei640-r-br-03
ipc-bei640-r-br-04
ipc-bei640-r-br-05
ipc-bem640-r-br-01
ipc-bem640-r-br-02
ipc-bem640-r-br-03
ipc-crg660-r-br-02
ipc-geb680-r-br-04
ipc-lgv630-r-br-01
This small little ruby script counts the lines of the file braslist.txt scans it with a regex and writes the results to a newfile called "strippedfile.txt"
lines = IO.readlines("/usr/local/bin/braslist.txt")
# Linecount is forwarded to StdOut.
puts lines.length
str = File.read('braslist.txt')
file_name = ['strippedfile.txt']
file_name.each do |file_name|
text = File.read(file_name)
new_contents = str.scan(/^ipc-(?<bng>[a-z]{3}\d{3})-r-br(?<nr>-\d{2})$/)
# open and write to a file with ruby
open('strippedfile.txt', 'w') { |f|
f.print new_contents
}
end
Now what i cant seem to fix, is in the new file "strippedfile" the results are always ["bei640", "-01"] ["bei640", "-02"] ["bei640", "-03"]
And i am trying to get all results in this format:
bei640-01
bei640-02
bei640-03
bei640-04
scan returns an array of matches, you probably want to join them:
- new_contents = str.scan(/^ipc-(?<bng>[a-z]{3}\d{3})-r-br(?<nr>-\d{2})$/)
+ new_contents = str.scan(/^ipc-(?<bng>[a-z]{3}\d{3})-r-br(?<nr>-\d{2})$/).map(&:join)
To print everything without quotes and brackets line by line:
- f.print new_contents
+ f.puts new_contents
Assuming your resultant array is
a = [["bei640", "-02"], ["bei640", "-03"]]
You can use join to get your desired result
a.map{|i| i.join } #=> ["bei640-02", "bei640-03"]
or use shortcut as mudasobwa answered
a.map(&:join) #=> ["bei640-02", "bei640-03"]
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
This question already has answers here:
How to dynamically create a local variable?
(4 answers)
Closed 3 years ago.
I am copying each line of a file to separate files, depending on the content. Each line begins with "foo" or "bar", and I want to read the first few characters of each line and dynamically change the file name variable.
readfile = File.open("myfile.txt", 'r')
file_foo = File.open("file1.txt", 'w')
file_bar = File.open("file2.txt", 'w')
for line in readfile
writefile = 'file_' + line[0..2]
writefile.write(line)
end
file_foo.close
file_bar.close
This throws an error, as the variable writefile refers to the string "file_foo" or "file_bar".
Suggestions for an elegant Rubyist solution? I couldn't see from the documentation how send method could be applied here if that is indeed the way to go.
Make a hash of files:
readfile = File.open("myfile.txt", 'r')
files = {
'foo' => File.open("file1.txt", 'w'),
'bar' => File.open("file2.txt", 'w')
}
for line in readfile
files[line[0..2]].write(line)
end
files.each {|k, v| v.close}
I think you are looking for eval. It will take a string and evaluate it as Ruby code in the current context. So your example becomes:
readfile = File.open("myfile.txt", 'r')
file_foo = File.open("file1.txt", 'w')
file_bar = File.open("file2.txt", 'w')
for line in readfile
eval('file_' + line[0..2]).write(line)
end
filefoo.close
filebar.close
However, you asked for a "Rubyist" approach. Using eval is certainly NOT a Rubyist approach. Nor is the use of for loops. I'll take a crack at a more Rubyist approach:
infile = "myfile.txt"
foofile = "file1.txt"
barfile = "file2.txt"
def append_to_file(path, content)
File.open(path, 'a') { |f| f << content }
end
IO.readlines(readfile).each do |line|
case line
when /^foo/
append_to_file(foofile, line)
when /^bar/
append_to_file(barfile, line)
end
end
You cannot use send because what you are trying to convert a string into is not a method but is a local variable.
From Ruby 2.1, you will be able to use Binding#local_variable_get.
for line in readfile
writefile = binding.local_variable_get(:"file_#{line[0..2]}")
writefile.write(line)
end