使用凯撒密码时如何保留字符大小写
How to preserve case of characters when using Caesar Cipher
我在 Ruby 中有一个凯撒密码脚本正在运行,但它 returns 字符串全部为大写字母,而不是保留原始字符串的大小写。
我可以使用 capitalize
使它看起来足够好,但我想要一种更具体的方式来保留案例。
这是脚本:
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)
如有任何帮助或见解,我们将不胜感激。
正如评论中所说,tr
更易于用于 Caesar Cypher(一旦您准备好两个字母表),而且速度也应该快得多:
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?")
根据您现有的代码,最简单的解决方案是检查字符是大写还是小写,然后相应地设置 base_ord
。由于 UTF-8 中小写字母在大写字母之后(如 ASCII),我们可以只测试 letter >= 'a'
,例如:
base_ord = (letter >= 'a' ? 'a' : 'A').ord
这是进行此更改的整个方法(您不再需要 BASE_ORD
常量):
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!
编辑
Amadan 关于使用 String#tr
提出了一个很好的观点。这是一个更简洁的实现:
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!
我在 Ruby 中有一个凯撒密码脚本正在运行,但它 returns 字符串全部为大写字母,而不是保留原始字符串的大小写。
我可以使用 capitalize
使它看起来足够好,但我想要一种更具体的方式来保留案例。
这是脚本:
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)
如有任何帮助或见解,我们将不胜感激。
正如评论中所说,tr
更易于用于 Caesar Cypher(一旦您准备好两个字母表),而且速度也应该快得多:
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?")
根据您现有的代码,最简单的解决方案是检查字符是大写还是小写,然后相应地设置 base_ord
。由于 UTF-8 中小写字母在大写字母之后(如 ASCII),我们可以只测试 letter >= 'a'
,例如:
base_ord = (letter >= 'a' ? 'a' : 'A').ord
这是进行此更改的整个方法(您不再需要 BASE_ORD
常量):
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!
编辑
Amadan 关于使用 String#tr
提出了一个很好的观点。这是一个更简洁的实现:
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!