I am trying to create a find and replace script in ruby. But I cannot figure out how to write to the same file twice when there are two conditions matched (2 different regex patterns are found and need to be replaced in the same file) I can get it to provide 2 copies of the file concatonated with only changes made from one condition in each.
Here is my code (Specifically pattern3 and pattern4):
print "What extension do you want to modify? "
ext = gets.chomp
if ext == "py"
print("Enter password: " )
pass = gets.chomp
elsif ext == "bat"
print "Enter drive letter: "
drive = gets.chomp
print "Enter IP address and Port: "
ipport = gets.chomp
end
pattern1 = /'Admin', '.+'/
pattern2 = /password='.+'/
pattern3 = /[a-zA-Z]:\\(?i:dir1\\dir2)/
pattern4 = /http:\/\/.+:\d\d\d\d\//
Dir.glob("**/*."+ext).each do |file|
data = File.read(file)
File.open(file, "w") do |f|
if data.match(pattern1)
match = data.match(pattern1)
replace = data.gsub(pattern1, '\''+pass+'\'')
f.write(replace)
puts "File " + file + " modified " + match.to_s
elsif data.match(pattern2)
match = data.match(pattern2)
replace = data.gsub(pattern2, 'password=\''+pass+'\'')
f.write(replace)
puts "File " + file + " modified " + match.to_s
end
if data.match(pattern3)
match = data.match(pattern3)
replace = data.gsub(pattern3, drive+':\dir1\dir2')
f.write(replace)
puts "File " + file + " modified " + match.to_s
if data.match(pattern4)
match = data.match(pattern4)
replace = data.gsub(pattern4, 'http://' + ipport + '/')
f.write(replace)
puts "File " + file + " modified " + match.to_s
end
end
end
end
f.truncate(0) makes things better but truncates the first line since it concatonates from the end of the 1st modified portion of the file.
Try writing file only once after all substitutions:
print "What extension do you want to modify? "
ext = gets.chomp
if ext == "py"
print("Enter password: " )
pass = gets.chomp
elsif ext == "bat"
print "Enter drive letter: "
drive = gets.chomp
print "Enter IP address and Port: "
ipport = gets.chomp
end
pattern1 = /'Admin', '.+'/
pattern2 = /password='.+'/
pattern3 = /[a-zA-Z]:\\(?i:dir1\\dir2)/
pattern4 = /http:\/\/.+:\d\d\d\d\//
Dir.glob("**/*.#{ext}").each do |file|
data = File.read(file)
data.gsub!(pattern1, "'#{pass}'")
data.gsub!(pattern2, "password='#{pass}'")
data.gsub!(pattern3, "#{drive}:\\dir1\\dir2")
data.gsub!(pattern4, "http://#{ipport}/")
File.open(file, 'w') {|f| f.write(data)}
end
Related
I need to replace placeholders NOUN, VERB, ADJ, and ADV in a file solution09.txt with user input.
Madlib solution09.txt:
One day I was watching my son [ADV] play with his [NOUN]. He was pretending the [NOUN] were [ADJ]. After a few minutes he was pretending to [VERB], because one of the [NOUN] drove away. When i asked him about it he [ADV] said, umm it's funny when [NOUN] [VERB] because [NOUN] can't really [VERB].
I think I successfully put the file into a string, but I have to read the string, and replace the placeholders with user input. Once I replace, I need to output the new madlib. I'm getting the user input into variables, but I'm not sure how to correctly replace the placeholder with the users input.
Current code:
file = File.open("solution09.txt", "r")
contents = file.read
puts "Enter a noun: "
noun = gets.chomp
puts "Enter a verb: "
verb = gets.chomp
puts "Enter an adjective: "
adj = gets.chomp
puts "Enter an adverb: "
adv = gets.chomp
if file.include?('NOUN')
file1= file.gsub("[NOUN]", noun, "[VERB]", verb, "ADJ", adj, "ADV", adv)
end
You can also build a replacement hash:
filename = "solution09.txt"
contents = File.read(filename)
replacements = {}
puts "Enter a noun: "
replacements['[NOUN]'] = gets.chomp
puts "Enter a verb: "
replacements['[VERB]'] = gets.chomp
puts "Enter an adjective: "
replacements['[ADJ]'] = gets.chomp
puts "Enter an adverb: "
replacements['[ADV]'] = gets.chomp
And pass it to gsub:
contents.gsub(Regexp.union(replacements.keys), replacements)
Regexp.union creates a pattern that matches any of the given keys.
Your code should look like
filename = "solution09.txt"
contents=File.read(filename)
puts "Enter a noun: "
noun=gets.chomp
puts "Enter a verb: "
verb=gets.chomp
puts "Enter an adjective: "
adj=gets.chomp
puts "Enter an adverb: "
adv=gets.chomp
if contents.include?('NOUN')
{ "\[NOUN\]" => noun,
"\[VERB\]" => verb,
"\[ADJ\]" => adj,
"\[ADV\]" => adv
}.each do |key, value|
contents.gsub!(key, value)
end
File.open(filename, "w") { |f| f << contents }
end
You need separate operation for read and write. There are other ways to do this
You can see how to do with single file pointer https://stackoverflow.com/a/10173112/1380263
You can also use ruby methods which interact with shell and use sed command (system, backticks, peopen)
Really depends on what suits your situation the best
file = File.read("solution09.txt")
.gsub(/\[(NOUN|VERB|ADJ|ADV)\]/) do
part = case $1
when "NOUN" then "a noun"
when "VERB" then "a verb"
when "ADJ" then "an adjective"
when "ADV" then "an adverb"
end
puts "Enter #{part}: "
gets.chomp
end
file_1 = File.open('Data_family.txt', 'r')
user1 = go
while user1 != "stop"
print "whould you like to create: "
user1 = gets.chomp
print "what is your relation: "
relation = gets.chomp
file_1.syswrite "this is your " + relation
file_1.syswrite "\n"
end
file_1.close
am am confused as to why by block does not run. I whould like to create a family tree data base yet it will not allow me to add data to the file i opened
I think you're trying to do something like:
#!/usr/bin/eval ruby
File.open('Data_family.txt', 'w') do |file_1|
loop do
print "Who would you like to create: "
user1 = gets.chomp
break if user1 == "stop"
print "what is your relation: "
relation = gets.chomp
file_1.puts "this is your " + relation
end
end #File autocloses at the end of the block
What does getc method do in ruby? I found this code:
class XOREncrypt
def initialize(inputfile, password, outputfile)
input = File.open(inputfile,"r")
pass_array = password.split(//)
output = File.new(outputfile,"w")
i = 0
while c = input.getc
pass_char = pass_array[i]
xor = c.chr[0] ^ pass_char[0]
output.print(xor.chr)
i+=1
if i == (pass_array.size - 1)
i = 0
end
end
input.close
output.close
end
end
puts "Filename for Input : "
inputfile = gets
puts "Insert Password : "
password = gets
puts "Filename for Output : "
outputfile = gets
XOREncrypt.new(inputfile.chomp, password.chomp, outputfile.chomp)
What does getc method do in ruby? I googled it, but without result.
reads the next 8 bit from the file.
http://ruby-doc.org/core-1.8.6/IO.html#method-i-getc
I have a text file with several different sections. Each section has a header followed by the actual data. For example:
Header1
x,y,z
x,y,z
x,y,z
Header2
a,b,c
a,b,c
a,b,c
I want to read through the file in one pass and do different things with the data present under each section. I know how to parse the data, but I'm having trouble figuring out how to code the logic for "Do this until hitting Header2, then do something else until Header3, etc."
I'm using ruby, and I haven't really come across any examples of doing this. Any suggestions?
At the simplest you could do something like this:
# Process lines for header1
def do_header1(line)
puts line.split(/,/).join("|")
end
# Process lines for header2
def do_header2(line)
puts line.split(/,/).map{ |e| e.upcase}.join(",")
end
header1 = false
header2 = false
# Main loop
File.open("file.txt").each_line do |line|
if line.chomp == 'Header1' # or whatever match for header1
header1 = true
header2 = false
next
end
if line.chomp == 'Header2' # or whatever match for header2
header1 = false
header2 = true
next
end
do_header1(line) && next if header1
do_header2(line) && next if header2
end
If the number of headers becomes too high, you can start tracking headers with an integer:
header = -1
# Main loop
File.open("file.txt").each_line do |line|
if line.chomp == 'Header1' # or whatever match for header1
header = 1
next
end
if line.chomp == 'Header2' # or whatever match for header2
header = 2
next
end
do_header1(line) && next if header == 1
do_header2(line) && next if header == 2
end
A solution using objects. For each line you ask each parser if a new section has started that the parser can parse.
class Section1Parser
def section? potential_header
potential_header.chomp == 'Header1'
end
def parse line
puts "Section 1: #{line.split(/,/).join("|")}"
end
end
class Section2Parser
def section? potential_header
potential_header.chomp == 'Header2'
end
def parse line
puts "Section 2: #{line.split(/,/).join("|")}"
end
end
parsers = [Section1Parser.new, Section2Parser.new]
selected_parser = nil
File.open("c:\\temp\\file.txt").each_line do |line|
if new_parser_detected = parsers.detect {|p| p.section? line }
selected_parser = new_parser_detected
next # skip header
end
selected_parser.parse line if selected_parser
end
Would something like this work?
File.open('datafile').each_line do |s|
if s =~ /^headerpattern$/
#Start a new parsing block
...
else
#Parse data
...
end
end
In my case 'Header' was in form of following string OBJECT ObjectType ObjectNumber ObjectName
if File.exist?("all.txt") then
object_file = File
File.open("all.txt").each_line do |line|
file_name = case
when line.match('^OBJECT Table.*')
"TAB" + line.split[2] + ".TXT"
when line.match('^OBJECT Form.*')
"FOR" + line.split[2] + ".TXT"
when line.match('^OBJECT Report.*')
"REP" + line.split[2] + ".TXT"
when line.match('^OBJECT Dataport.*')
"DAT" + line.split[2] + ".TXT"
when line.match('^OBJECT XMLPort.*')
"XML" + line.split[2] + ".TXT"
when line.match('^OBJECT Codeunit.*')
"COD" + line.split[2] + ".TXT"
when line.match("^OBJECT MenuSuite.*")
"MEN" + line.split[2] + ".TXT"
when line.match('^OBJECT Page.*')
"PAG" + line.split[2] + ".TXT"
when line.match('^OBJECT Query.*')
"QUE" + line.split[2] + ".TXT"
end
unless file_name.nil?
File.exist?(file_name) { File.delete(file_name) }
object_file = File.open(file_name,"w")
end
object_file.write(line)
end
end
But there are some prerequisites: I'm always sure that first line of the file will contain a header. I'm also not closing file (this will definitely draw my karma to the zero one day).
I wrote a script that I decided to refactor so I could add functionality to it as my coworkers think of it. I only saved four lines in the effort, but the main change is I removed both methods and reduced the number of called variables in favor of string interpolation/manipulation. Is there a preference for this? Is it better to declare a new variable just to use once, or is it more DRY to just make minor tweaks to the string when you need to use it? For example here is the original code:
def validate_directory(dir)
puts "Enter the full directory path of the flv files." unless dir
input = dir || gets.chomp
input.gsub!('\\', '/')
input += '/' unless input[-1..-1] == '/'
until File.directory?(input) && Dir.glob("#{input}*.flv") != []
puts "That directory either doesn't exist or contains no .flv files. \nEnter the full directory path of the flv files."
input = $stdin.gets.chomp
input.gsub!('\\', '/')
input += '/' unless input[-1..-1] == '/'
end
dir = input
end
def output(flv, location)
title = flv.dup.gsub!(".flv", ".html")
vid = flv.dup
vid.slice!(0..6)
body = $EMBED.gsub("sample.flv", vid)
htmlOutput = File.open(title, "w")
htmlOutput.write(body)
htmlOutput.close
linkList = File.open("#{location}List of Links.txt", "a")
linkList.write($BASE + vid.gsub(".flv", ".html") + "\n")
linkList.close
puts "Files created successfully."
end
dir = ARGV[0].dup unless ARGV.empty?
folder = validate_directory(dir)
files = folder.clone + "*.flv"
flvs = Dir.glob("#{files}")
File.delete("#{folder}List of Links.txt") if File.exists?("#{folder}List of Links.txt")
flvs.each { |flv| output(flv, folder) }
And the new stuff:
flash_folder = ARGV[0].dup unless ARGV.empty?
if !flash_folder
puts "Enter the full directory path of the flv files."
flash_folder = gets.chomp
end
flash_folder.gsub!('\\', '/')
flash_folder += '/' unless flash_folder[-1..-1] == '/'
until File.directory?(flash_folder) && Dir.glob("#{flash_folder}*.flv") != []
puts "That directory either doesn't exist or contains no .flv files. \nEnter the full directory path of the flv files."
flash_folder = $stdin.gets.chomp
flash_folder.gsub!('\\', '/')
flash_folder += '/' unless flash_folder[-1..-1] == '/'
end
flash_files = Dir.glob("#{flash_folder}*.flv")
File.delete("#{flash_folder}List of Links.txt") if File.exists?("#{flash_folder}List of Links.txt")
flash_files.each do |flv|
html_output = File.open("#{flv.gsub(".flv", ".html")}", "w")
html_output.write("#{embed_code.gsub("sample.flv", flv.slice(7..flv.length))}")
html_output.close
link_list = File.open("#{flash_folder}List of Links.txt", "a")
link_list.write("#{flash_url}#{flv.slice(2..flv.length).gsub(".flv", ".html")}\n")
link_list.close
end
puts "Finished."