I'm trying to execute this program from the command line, and I'm not able to use gets.chomp, instead, it returns the key value.
I am entering: ruby name_of_file.rb name_of_file.txt
def caesar_cipher(key)
s = gets.chomp
encoded = ""
s.each_byte do |l|
if ((l >= 65 && l <= 90) || (l >= 97 && l <= 122))
encoded += (l+key).chr
else
encoded += l.chr
end
end
encoded
end
File.readlines(ARGV[0]).map(&:to_i).each {|key| puts caesar_cipher(key)}
I know the program does not execute the caesar cipher completely, I am just trying to figure out how to run it from the command line without having to use pry or irb.
You want to manually enter the cipher key?
Use STDIN.gets
#vgoff has the answer, but here's how I'd rewrite the the code to be more readable:
def caesar_cipher(key)
encoded = ""
s = STDIN.gets.chomp
s.each_char do |l|
case l
when 'A' .. 'Z', 'a' .. 'z'
encoded += (l.ord + key).chr
else
encoded += l
end
end
encoded
end
# File.readlines(ARGV[0]).map(&:to_i).each {|key| puts caesar_cipher(key)}
puts caesar_cipher(0)
puts caesar_cipher(1)
Instead of splitting characters into bytes, I'd probably use each_char to maintain the character-encoding. I'd use a case statement to let me use two ranges to define upper and lower-case characters cleanly, and use ord to get the actual ordinal value for a character, instead of the byte.
It's more readable, but might not fully satisfy your needs.
Related
I'm new at this and have been practicing on codewars- one of the challenges is a caesar cypher challenge and I've got it pretty much solved except for this one issue...
Here's the code I wrote:
def rot13(string)
alpha = "abcdefghijklmnopqrstuvwxyz"
arr = []
string.each_char.with_index do |char, i|
n = alpha.index(char.downcase)
a = (n.to_i + 13) % 26
unless alpha.include?(char.downcase)
arr << char
end
if char == char.upcase
arr << alpha[a].upcase
else
arr << alpha[a]
end
end
return arr.join
end
puts rot13('10+2 is twelve')
and when I run my code it comes back as this-
1N0N+N2N Nvf Ngjryir
Why are the Ns showing up? Anyone know?
The issue is that you're not checking for non-alphabetical characters properly. Going through the logic:
At line 5, you set n to the index of the character in your alphabet string alpha. Because only letters are included, this call to #include? returns nil for all characters that aren't letters (e.g. your string's first character, '1').
At line 6, you set a to n.to_i + 13. Because nil.to_i is zero, this will always be 13 for any character that isn't a letter.
In the block in lines 7-9, you push char to your array because it doesn't exist in alpha (and so you get '1' as the first character of your output).
But then, in line 10, you push alpha[a].upcase as well if char == char.upcase. If char isn't a letter (e.g. initially '1'), it passes this test (because '1' == '1'.upcase) and your code pushes alpha[13].upcase to the output as well, and 'N' is the 13th letter of the alphabet.
Basically, your checks aren't sufficient. You need to account for numbers and other non-alphabetical characters properly each time.
I'm trying to solve a problem where when given a string I convert each letter 13 places further in the alphabet. For example
a => n
b => o
c => p
Basically every letter in the string is converted 13 alphabet spaces.
If given the string 'sentence' i'd like it to convert to
'feagrapr'
I have no idea how to do it. I've tried
'sentence'.each_char.select{|x| 13.times{x.next}}
and I still couldn't solve it.
This one has been puzzling me for a while now, and I've given up trying to solve it.
I need your help
IMHO, there is a better way to achieve the same in idiomatic Ruby:
def rot13(string)
string.tr("A-Za-z", "N-ZA-Mn-za-m")
end
This works because the parameter 13 is hard-coded in the OP's question, in which case the tr function seems to be just the right tool for the job!
Using String#tr as TCSGrad suggests is the ideal solution.
Some alternatives:
Using case, ord, and chr
word = 'sentence'
word.gsub(/./) do |c|
case c
when 'a'..'m', 'A'..'M' then (c.ord + 13).chr
when 'n'..'z', 'N'..'Z' then (c.ord - 13).chr
else c
end
end
Using gsub and a hash for multiple replacement
word = 'sentence'
from = [*'a'..'z', *'A'..'Z']
to = [*'n'..'z', *'a'..'m', *'N'..'Z', *'A'..'M']
cipher = from.zip(to).to_h
word.gsub(/[a-zA-Z]/, cipher)
Note, Array#to_h requires Ruby 2.1+. For older versions of Ruby, use
cipher = Hash[from.zip(to)].
From here -> How do I increment/decrement a character in Ruby for all possible values?
you should do it like:
def increment_char(char)
return 'a' if char == 'z'
char.ord.next.chr
end
def increment_by_13(str)
conc = []
tmp = ''
str.split('').each do |c|
tmp = c
13.times.map{ |i| tmp = increment_char(tmp) }
conc << tmp
end
conc
end
Or close.
I am looking to extract all Methionine residues to the end from a sequence.
In the below sequence:
MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG
Original Amino Acid sequence:
atgtttgaaatcgaagaacatatgaaggattcacaggtggaatacataattggccttcataatatcccattattgaatgcaactatttcagtgaagtgcacaggatttcaaagaactatgaatatgcaaggttgtgctaataaatttatgcaaagacattatgagaatcccctgacgggg
I want to extract from the sequence any M residue to the end, and obtain the following:
- MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG
- MKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG
- MNMQGCANKFMQRHYENPLTG
- MQGCANKFMQRHYENPLTG
- MQRHYENPLTG
With the data I am working with there are cases where there are a lot more "M" residues in the sequence.
The script I currently have is below. This script translates the genomic data first and then works with the amino acid sequences. This does the first two extractions but nothing further.
I have tried to repeat the same scan method after the second scan (See the commented part in the script below) but this just gives me an error:
private method scan called for #<Array:0x7f80884c84b0> No Method Error
I understand I need to make a loop of some kind and have tried, but all in vain. I have also tried matching but I haven't been able to do so - I think that you cannot match overlapping characters a single match method but then again I'm only a beginner...
So here is the script I'm using:
#!/usr/bin/env ruby
require "bio"
def extract_open_reading_frames(input)
file_output = File.new("./output.aa", "w")
input.each_entry do |entry|
i = 1
entry.naseq.translate(1).scan(/M\w*/i) do |orf1|
file_output.puts ">#{entry.definition.to_s} 5\'3\' frame 1:#{i}\n#{orf1}"
i = i + 1
orf1.scan(/.(M\w*)/i) do |orf2|
file_output.puts ">#{entry.definition.to_s} 5\'3\' frame 1:#{i}\n#{orf2}"
i = i + 1
# orf2.scan(/.(M\w*)/i) do |orf3|
# file_output.puts ">#{entry.definition.to_s} 5\'3\' frame 1:#{i}\n#{orf3}"
# i = i + 1
# end
end
end
end
file_output.close
end
biofastafile = Bio::FlatFile.new(Bio::FastaFormat, ARGF)
extract_open_reading_frames(biofastafile)
The script has to be in Ruby since this is part of a much longer script that is in Ruby.
You can do:
str = "MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG"
str.scan(/(?=(M.*))./).flatten
#=> ["MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG", MKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG", "MNMQGCANKFMQRHYENPLTG", "MQGCANKFMQRHYENPLTG", "MQRHYENPLTG"]
This works by capturing loookaheads starting with M and advancing one char at a time.
str = "MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG"
pos = 0
while pos < str.size
if md = str.match(/M.*/, pos)
puts md[0]
pos = md.offset(0)[0] + 1
else
break
end
end
--output:--
MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG
MKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG
MNMQGCANKFMQRHYENPLTG
MQGCANKFMQRHYENPLTG
MQRHYENPLTG
md -- stands for the MatchData object.
match() -- returns nil if there is no match, the second argument is the start position of the search.
md[0] -- is the whole match (md[1] would be the first parenthesized group, etc.).
md.offset(n) -- returns an array containing the beginning and ending position in the string of md[n].
Running the program on the string "MMMM" produces the output:
MMMM
MMM
MM
M
I have also tried matching but I haven't been able to do so - I think
that you cannot match overlapping characters a single match method but
then again I'm only a beginner...
Yes, that's true. String#scan will not find overlapping matches. After scan finds a match, the search continues from the end of the match. Perl has some ways to make regexes back-up, I don't know whether Ruby has those.
Edit:
For Ruby 1.8.7:
str = "MFEIEEHMKDSQVEYIIGLHNIPLLNATISVKCTGFQRTMNMQGCANKFMQRHYENPLTG"
pos = 0
while true
str = str[pos..-1]
if md = str.match(/M.*/)
puts md[0]
pos = md.offset(0)[0] + 1
else
break
end
end
What's wrong with my code? Is FileNameArray being reused?
f.rb:17: warning: already initialized constant FileNameArray
number = 0
while number < 99
number = number + 1
if number <= 9
numbers = "000" + number.to_s
elsif
numbers = "00" + number.to_s
end
files = Dir.glob("/home/product/" + numbers + "/*/*.txt")
files.each do |file_name|
File.open(file_name,"r:utf-8").each do | txt |
if txt =~ /http:\/\//
if txt =~ /static.abc.com/ or txt =~ /static0[1-9].abc.com/
elsif
$find = txt
FileNameArray = file_name.split('/')
f = File.open("error.txt", 'a+')
f.puts FileNameArray[8], txt , "\n"
f.close
end
end
end
end
end
You might be a ruby beginner, I tried to rewrite the same code in ruby way...
(1..99).each do |number|
Dir.glob("/home/product/" + ("%04d" % numbers) + "/*/*.txt").each do |file_name|
File.open(file_name,"r:utf-8").each do | txt |
next unless txt =~ /http:\/\//
next if txt =~ /static.abc.com/ || txt =~ /static0[1-9].abc.com/
$find = txt
file_name_array = file_name.split('/')
f = File.open("error.txt", 'a+')
f.puts file_name_array[8], txt , "\n"
f.close
end
end
end
Points to note down,
In ruby if you use a variable prefixed with $ symbol, it is taken as a global variable. So use $find, only if it is required.
In ruby a constant variable starts with capital letter, usually we are NOT supposed to change a constant value. This might have caused the error in your program.
(1..99) is a literal used to create instance of Range class, which returns values from 1 to 99
In Ruby variable name case matters. Local variables must start with a lower case character. Constants - with an upper case.
So, please try to rename FileNameArray to fileNameArray.
Also, glob takes advanced expressions that can save you one loop and a dozen of LOCs. In your case this expression should look something like:
Dir.glob("/home/product/00[0-9][0-9]/*/*.txt")
I would like to capitalize each word of a UTF-8 string. However, I need the function to ignore some special characters in the beginning of words, like "(-.,". The function will be used to capitalize song titles which can look like this:
marko, gabriel boni, simple jack - recall (original mix)
...would output:
Marko, Gabriel Boni, Simple Jack - Recall (Original Mix)
It should also be able to capitalize UTF-8 chars like "å" > "Å". "é" > "É".
Is there something why Unicode::capitalize method from unicode library does not suit your needs ?
irb(main):013:0> require 'unicode'
=> true
irb(main):014:0> begin Unicode::capitalize 'åäöéèí' rescue $stderr.print "unicode error\n" end
=> "Åäöéèí"
irb(main):015:0> begin Unicode::capitalize '-åäöéèí' rescue $stderr.print "unicode error\n" end
=> "-åäöéèí"
"åbc".mb_chars.capitalize
#=> "Åbc"
"ébc".mb_chars.capitalize.to_s
#=> "Ébc"
UPD
And to ignore none word chars:
string = "-åbc"
str = string.match(/^(\W*)(.*)/)
str[1] + str[2].mb_chars.capitalize.to_s
#=> "-Åbc"
I did this and wanted to filter a lot of things.
I created a constants file initializers/constants.rb
letters = ("a".."z").collect
numbers = ("1".."9").collect
symbols = %w[! # # $ % ^ & * ( ) _ - + = | \] { } : ; ' " ? / > . < , ]
FILTER = letters + numbers + symbols
And then just did a check to see if it was in my filter:
if !FILTER.include?(c)
#no
else
#yes
end
You can also check the value of the unicode but you need to know the range or specific values. I did this with chinese characters, so that is where I got my values. I will post some code just to give you an idea:
def check(char)
char = char.unpack('U*').first
if char >= 0x4E00 && char <= 0x9FFF
return true
end
if char >= 0x3400 && char <= 0x4DBF
return true
end
if char >= 0x20000 && char <= 0x2A6DF
return true
end
if char >= 0x2A700 && char <= 0x2B73F
return true
end
return false
end
You need to know the specific values here of course.