You are given string $S
containing alphabets A..Z
only and a number $N
.
Write a script to encrypt the given string $S
using Caesar Cipher with left shift of size $N
.
Overview
Wikipedia has a description of the Ceasar Cipher.
First thing we need to define is how to read the input. We’ve decided to read the plain text from standard input, and we use a command line option (-s SHIFT
) to indicate the left shift. The encrypted text will be written to standard output.
Shifting will be done by looking at each characters, and if it is a capital letter, we look at its code point (ASCII or Unicode — which for letters A
to Z
is identical), and subtract the left shift. If the result is less than 65, we add 26. 65 being the code point of A
, and 26 the number of letters. We than map the resulting code point back to a character.
Solutions
Perl
use Getopt::Long; GetOptions 's=i' => \my $shift; die "-s SHIFT option required" unless defined $shift; $shift %= $NR_OF_LETTERS;
We first parse to the command line. Getopt::Long
does the work of parsing @ARGV
for us. If no option is given, we die. Else, we mode the value (now in $shift
) with 26
, because shifting by 26
is a no-op.
We can now do the actual shifting:
my $NR_OF_LETTERS = 26; my $ORD_A = ord ('A'); while (<>) { chomp; s {([A-Z])} { my $ch = ord ($1) - $shift; $ch += $NR_OF_LETTERS if $ch < $ORD_A; chr $ch }eg; say; }
We iterate over the input, and for each capital letter found, we take its code point (ord
), and subtract the given shift value. We add 26 if we end up below the code point of the letter A
. Then we translate the number back to a (one letter) string (chr
).
Find the complete program on GitHub.
AWK
AWK does not have a method to map a character to its code point. We therefor start by building an array (ord
), so we can look up the code point. In AWK, we can index arrays with strings:
BEGIN { NR_OF_LETTERS = 26 ORD_A = 65 for (i = ORD_A; i < ORD_A + NR_OF_LETTERS; i ++) { t = sprintf ("%c", i) ord [t] = i } }
We then need to parse the command line option:
BEGIN { for (i = 1; i < ARGC; i ++) { if (ARGV [i] == "-s") { shift = ARGV [i + 1] } } ARGC = 0 }
ARGC
is the number of elements in ARGV
, which, unlike most arrays in AWK, is indexed starting from 0
. We’re scanning the array starting from index 1, as ARGV [0]
contains the name of the executable (typically awk
).
Note the assignment ARGC = 0
. If we don’t do this, awk
itself will treat the options as subsequent files to be executed.
Now the shifting of the characters:
{ out = "" for (i = 1; i <= length (letters); i ++) { char = substr ($0, i, 1) if (ord [char]) { n = ord [char] - shift if (n < ORD_A) { n = n + NR_OF_LETTERS } char = sprintf ("%c", n) } out = out char } print out }
AWK does not have a direct way of mapping a code point to a string (like chr
in Perl and several other languages), but it does have sprintf
, which has a %c
specifier in the format, which does exactly that job. With AWK, sprintf
works exactly the same as its C counterpart.
Find the complete program on GitHub.
Bash
First, getting the option:
while getopts "s:" name do if [ "$name" = "s" ] then shift=$OPTARG fi done
Bash (and the Bourne Shell) has a getopts
buildin, which parses command line options. Here, we’re using the format "s:"
to indicate we’re looking for a -s
parameter with a mandatory option. We can iterate over the results of getopts
, which sets the option in a variable $name
in each iteration. The value belonging to that option is in $OPTARG
.
To shift the characters, we use a different algorithm that we’re using in the other languages. Bash doesn’t have easy methods to get the code point of a character, or to get the character given a code point. So, we’re using tr
to shift the text one character to the left — and we do this as often as needed:
while read line do for ((i = 0; i < $shift; i ++)) do line=`echo $line | tr A-Z ZA-Y` done echo $line done
Find the complete program on GitHub.
C
C has a getopt
function, which like the Bash buildin, can be iterated over. It takes the same format string as we used in Bash, "s:"
, indicating a -s
parameter taking an option. It take two further arguments, a number argc
and an array of strings argv
; the first is the length of the array argc
, and argv
contains the name of the program as the first element, and the arguments to the program as subsequent elements. Typically, argc
and argv
are just copied from the parameters to main ()
:
int main (int argc, char ** argv) { int ch; int shift = -1; int NR_OF_LETTERS = 26; while ((ch = getopt (argc, argv, "s:")) != -1) { switch (ch) { case 's': shift = atoi (optarg) % NR_OF_LETTERS; break; } } if (shift < 0) { fprintf (stderr, "Requires an -s parameter\n"); exit (1); }
C doesn’t automagically convert between integers and strings, so we’re calling atoi
on the -s
argument; atoi
takes a string, and returns an integer.
In C, strings are just arrays of numbers, so shifting is easy. We can directly modify the numeric value in the array. We’re using getline
to read lines from standard input. We’re then using a moving pointer, line_ptr
to look (and modify) each character in the string line
. If line_ptr
points to a capital letter — which we check using the function isupper
, we shift the character the required amount:
while ((strlen = getline (&line, &len, stdin)) != -1) { char * line_ptr = line; while (* line_ptr) { if (isupper (* line_ptr)) { * line_ptr -= shift; if (* line_ptr < 'A') { * line_ptr += NR_OF_LETTERS; } } line_ptr ++; } printf ("%s", line); }
Find the complete program on GitHub.
Lua
In our Lua solution, we’re parsing the command line arguments ourselves. The array with arguments is called arg
, and prefixing an array with #
gives its size:
local NR_OF_LETTERS = 26 local shift = -1 if #arg == 2 and arg [1] == "-s" then shift = arg [2] % NR_OF_LETTERS end if shift < 0 then io . stderr : write ("Requires a '-s SHIFT' option\n") os . exit (1) end
We define a function to shift a capital character; it takes two arguments, char
, the character to be shifted, and shift
, the distance to shift:
local ORD_A = string . byte ("A") function do_shift (char, shift) local n = string . byte (char) - shift if n < ORD_A then n = n + NR_OF_LETTERS end return string . char (n) end
Note the byte
and char
functions from the string
class. The first returns the code point of the character passed in; the latter does the reverse, returning the character of the code point passed in.
We then iterate over the input, and use the gsub
method to shift capital letters. gsub
does a global substitution, and can take a function as parameter. If so, for each match, the function is called, and its return value is used as the substitution part. This is more or less equivalent to Perls s///e
.
for line in io . lines () do io . write (string . gsub (line, "[A-Z]", function (ch) return do_shift (ch, shift) end), "\n") end
Find the complete program on GitHub.
Node.js
Node.js has an impressive module to parse command line arguments, yargs
, which we will be using the parse the command line:
const argv = require ('yargs') . option ('s', { type: 'number', }) . demandOption ('s') . argv; const shift = argv . s
A function to shift an individual character:
const NR_OF_LETTERS = 26 const ORD_A = "A" . charCodeAt (0) function shift_char (char, shift) { if (char . match (/[A-Z]/)) { let n = char . charCodeAt (0) - (shift % NR_OF_LETTERS) if (n < ORD_A) { n = n + NR_OF_LETTERS } return String . fromCharCode (n) } else { return char } }
Note that here, unlike with our Lua solution, the function is called for any character in the input, so we need an additional check so we only shift capital letters. To get the code point of a character, we call charCodeAt
; to get the character given a code point, we use fromCharCode
.
To process the input, we split each line into individual characters, call shift_char
on each of them, then join the return values back to a string:
require ('readline') . createInterface ({input: process . stdin}) . on ('line', _ => console . log (_ . split ("") . map (_ => shift_char (_, shift)) . join ("")))
Find the complete program on GitHub.
Python
Python has a getopt
module which has a getopt
function which is similar to the one used in C and Bash. (Python also has argparse
, but we stick with something we’re more familiar with).
NR_OF_LETTERS = 26 shift = -1 opts, args = getopt . getopt (sys . argv [1:], 's:') for opt, val in opts: if opt == "-s": shift = int (val) % NR_OF_LETTERS sys . argv [1:] = [] if shift < 0: sys . stderr . write ("Argument -s SHIFT is required\n") sys . exit (1)
Note the assignment sys . argv [1:] = []
, this clears the argv
array. That way, we will be reading from standard input instead of Python trying to open a file called -s
.
Strings are immutable in Python, so we will be construction a new string, with the capital letters shifted:
for line in fileinput . input (): out = "" for i in range (len (line)): if "A" <= line [i] <= "Z": n = ord (line [i]) - shift if n < ORD_A: n = n + NR_OF_LETTERS out += chr (n) else: out += line [i] sys . stdout . write (out)
Find the complete program on GitHub.
Ruby
Ruby has the optparse
module to parse command line parameters. This supplies a method getopts
which returns an hash with options and their values.
require 'optparse' NR_OF_LETTERS = 26 params = ARGV . getopts ('s:') shift = params ["s"] ? params ["s"] . to_i % NR_OF_LETTERS : -1 if shift < 0 STDERR . puts "Requires a -s SHIFT option" exit 1 end
We’re using to_i
to make the parameter a string.
A function to shift a capital letter:
def shift_letter (letter, shift) n = letter . ord - shift if n < 'A' . ord then n = n + NR_OF_LETTERS end return n . chr end
We’re iterating over the input, and are using gsub
to replace each capital letter with a shifted one:
ARGF . each_line do |line| line = line . gsub (/[A-Z]/) {|_| shift_letter _, shift} puts line end
Find the complete program on GitHub.