How to preserve case of characters when using Caesar Cipher

谁说胖子不能爱 提交于 2020-01-25 02:47:10

问题


I have a Caesar Cipher script in Ruby that is working but it returns the string as all upper-case letters instead of preserving the cases of the original string.

I could use capitalize to make it look good enough but I would like a more concrete way of preserving the cases.

Here is the script:

BASE_ORD = 'A'.ord

def caesar_cipher(phrase, key)
  cipher = phrase.gsub(/[a-z]/i) do |c|
    orig_pos = c.upcase.ord - BASE_ORD
    new_pos = (orig_pos + key) % 26
    (new_pos + BASE_ORD).chr
  end
  puts cipher
end

caesar_cipher("What a string!", 5) 

Any help or insight would be appreciated.


回答1:


The simplest solution, given your existing code, is to check whether the character is uppercase or lowercase and set base_ord accordingly. Since the lowercase letters come after the uppercase letters in UTF-8 (as in ASCII), we can just test letter >= 'a', e.g.:

base_ord = (letter >= 'a' ? 'a' : 'A').ord

Here's the whole method with this change (you no longer need the BASE_ORD constant):

def caesar_cipher(phrase, key)
  phrase.gsub(/[a-z]/i) do |letter|
    base_ord = (letter >= 'a' ? 'a' : 'A').ord
    orig_pos = letter.ord - base_ord
    new_pos = (orig_pos + key) % 26
    (new_pos + base_ord).chr
  end
end

puts caesar_cipher("What a string!", 5) # => Bmfy f xywnsl!

Edit

Amadan makes a good point about using String#tr. Here's a somewhat more concise implementation:

ALPHABET = "aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"
# Or if you want to be fancy: ALPHABET = (?a..?z).flat_map {|c| [ c, c.upcase ] }.join

def caesar_cipher(phrase, key)
  to_alphabet = ALPHABET.dup
  to_alphabet << to_alphabet.slice!(0, key * 2)
  phrase.tr(ALPHABET, to_alphabet)
end

puts caesar_cipher("What a string!", 5) # => Bmfy f xywnsl!



回答2:


As said in comments, tr is easier to use for Caesar Cypher (once you prepare the two alphabets), and should also be much faster:

class CaesarCypher
    def initialize(key, alphabet=nil)
        @from_alphabet = alphabet || (?a..?z).to_a.join
        @to_alphabet = @from_alphabet[key..-1] + @from_alphabet[0...key]
        @from_alphabet += @from_alphabet.upcase
        @to_alphabet += @to_alphabet.upcase
    end
    def encode(str)
        str.tr(@from_alphabet, @to_alphabet)
    end
    def encode!(str)
        str.tr!(@from_alphabet, @to_alphabet)
    end
    def decode(str)
        str.tr(@to_alphabet, @from_alphabet)
    end
    def decode(str)
        str.tr!(@to_alphabet, @from_alphabet)
    end
end

cc = CaesarCypher.new(1)
puts cc.encode("Caesar, huh?")
puts cc.decode("Dbftbs, ivi?")


来源:https://stackoverflow.com/questions/34277950/how-to-preserve-case-of-characters-when-using-caesar-cipher

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!