I'm having a problem with CS50's substitution cipher problem. I'm stuck on how to validate the key. Whenever I pass a 26 character key as a command-line argument, the program outputs "you must not repeat any characters" even when the key doesn't have any. My program correctly checks for the length of the key and the presence of a command-line argument. It just doesn't acknowledge a valid nonrepeating key.
#include <stdio.h>
#include <cs50.h>
#include <string.h>
#include <ctype.h>
bool validateKey(char key[]);
string substitute(char key[], string plaintext);
int main(int argc, string argv[]) {
if(strlen(argv[1]) == 26) { //key typed after prgm name will be used to encrypt data
if(validateKey(argv[1])) {
string plaintext = get_string("Plaintext: ");
string ciphertext = substitute(argv[1], plaintext);
printf("Ciphertext: %s", ciphertext);
}
}
else if(argv[1] == NULL) {
printf("Usage: ./substitution key\n");
}
else {
printf("Key must contain 26 characters.\n");
}
}
bool validateKey(char key[]) {
for(int i = 0; i < 26; i++) {
if(!isalpha(key[i])) {
printf("Key must only contain alphabetic characters.\n");
return false;
}
}
/*
an array of counters to keep track of how many times a letter occurs in the cipher
each counter should be set to 1 if key doesn't have repeating letters
*/
int cntr[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
for (int i = 0; i < 26; i++) {
key[i] = islower(key[i]); //make all the letters in the key lowercase to make it easier to work with
switch(key[i]) {
case 'a':
cntr[0] += 1;
case 'b':
cntr[1] += 1;
case 'c':
cntr[2] += 1;
case 'd':
cntr[3] += 1;
case 'e':
cntr[4] += 1;
case 'f':
cntr[5] += 1;
case 'g':
cntr[6] += 1;
case 'h':
cntr[7] += 1;
case 'i':
cntr[8] += 1;
case 'j':
cntr[9] += 1;
case 'k':
cntr[10] += 1;
case 'l':
cntr[11] += 1;
case 'm':
cntr[12] += 1;
case 'n':
cntr[13] += 1;
case 'o':
cntr[14] += 1;
case 'p':
cntr[15] += 1;
case 'q':
cntr[16] += 1;
case 'r':
cntr[17] += 1;
case 's':
cntr[18] += 1;
case 't':
cntr[19] += 1;
case 'u':
cntr[20] += 1;
case 'v':
cntr[21] += 1;
case 'w':
cntr[22] += 1;
case 'x':
cntr[23] += 1;
case 'y':
cntr[24] += 1;
case 'z':
cntr[25] += 1;
}
}
for(int i = 0; i < 26; i++) {
if(cntr[i] != 1) {
printf("Key must not contain repeated characters.\n");
return false;
}
}
return true;
}
string substitute(char key[]) {
return "";
}
The "minimal" fix is to add a break after each increment, so your case 'a' doesn't execute the code for case 'b' and onwards. You'd also need to change the islower call to tolower (otherwise all your key values just become 0 and 1).
That said, the switch statement itself is already ridiculously overlong, and should really just be simplified to the one-liner:
cntr[tolower(key[i]) - 'a'] += 1;
which is safe, since you've already checked all the input characters pass isalpha.
Related
I have a task to make smth like t9 like it was in old phones (number can also mean some combination of letters). For example, if user run it this way ./t9search 23 < list.txt, program should return numbers which has this combination of digits or letters that corresponds to them (in this case a,b,c and d,e,f). It's also forbidden to work with dynamic memory(malloc, free) and functions with algorithms (qsort, lsearch etc). Format of information in file is "name\n number\n name\n number\n...".
The loop doesn't work correctly but I can't find a mistake. The program should return {Name}, {number}but it return only numbers.
The code is:
`
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define FULFILLED 1
#define NOT_FULFILLED 0
int main(int argc, char **argv)
{
char line[99];
int line_no = -1;
int size = strlen(argv[1]);
char letters[size][5];
int total_combinations = 1;
char combinations[total_combinations][size + 1];
int current_combination[size];
int check_name = NOT_FULFILLED;
char name[99];
for(int i = 0; argv[1][i]; i++)
{
switch(argv[1][i]) //cases according to input
{
case '0':
strcpy(letters[i], "+");
break;
case '2':
strcpy(letters[i], "abc");
break;
case '3':
strcpy(letters[i], "def");
break;
case '4':
strcpy(letters[i], "ghi");
break;
case '5':
strcpy(letters[i], "jkl");
break;
case '6':
strcpy(letters[i], "mno");
break;
case '7':
strcpy(letters[i], "pqrs");
break;
case '8':
strcpy(letters[i], "tuv");
break;
case '9':
strcpy(letters[i], "wxyz");
break;
}
total_combinations *= strlen(letters[i]); //number of combinations of letters from array elements
}
for (int i = 0; i < size; i++)
{
current_combination[i] = 0;
}
int k = 0;
while (k < total_combinations)
{
for (int set_idx = 0; set_idx < size; set_idx++)
{
int letter_idx = current_combination[set_idx];
combinations[k][set_idx] = letters[set_idx][letter_idx];
}
combinations[k][size] = '\0';
for(int i = size - 1; i >= 0; i--)
{
current_combination[i]++;
if (current_combination[i] == strlen(letters[i]))
{
current_combination[i] = 0;
}
else
{
break;
}
}
k++;
}
printf("%c\n", sizeof(combinations)/sizeof(combinations[0]));
while(fgets(line, sizeof(line), stdin))
{
line_no++;
if (line_no % 2 == 0) //even lines (names)
{
name[99] = line; //variable to use name with number
for (int i = 0; i < total_combinations; i++) //for each combination from array with combinations
{
if (strstr(line, combinations[i])) //if combination is a substring of a string
{
check_name = FULFILLED;
}
}
}
else if (line_no % 2 == 1) //odd lines (numbers)
{
if (check_name == FULFILLED || strstr(line, argv[1])) // if there's name that corresponds with input OR input is a substring of a string
{
printf("%s, %s", name, line);
check_name = NOT_FULFILLED;
}
}
}
}
`
I've tried to change the way of checking names:
if (line_no % 2 == 0)
{
for (int j = 0; j <= strlen(letters); j++)
{
char let = getchar();
for (int i = 0; i < strlen(line); i++)
{
if (letters[j] == line[i])
{
check_name = FULFILLED;
continue;
}
else if (letters[j] != line [i])
{
break;
}
}
}
}
Sure I didn't reach success. So I'd be extremely grateful for any help.
Arrays do not resize reflectively when the variables used to create them change. They are sized permanently at the time of their initialization. The following
int total_combinations = 1;
char combinations[total_combinations][size + 1];
/* ... */
total_combinations *= strlen(letters[i]);
does not cause combinations to grow as total_combinations does. Move the definition of combinations to after total_combinations has been fully computed.
The following attempts to assign a char * to a char, and also indexes name out of bounds.
name[99] = line;
As done previously, use strcpy to copy strings.
Note that, if found and if there is room in the buffer, fgets stores the newline character in the buffer, so
printf("%s, %s", name, line);
will very likely print staggered output, like
Alice
, 5551234567
A cursory refactoring:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *remove_newline(char *string)
{
if (string)
string[strcspn(string, "\n")] = '\0';
return string;
}
int main(int argc, char **argv)
{
if (argc < 2) {
fprintf(stderr, "usage: %s t9_sequence\n", argv[0]);
return EXIT_FAILURE;
}
int size = strlen(argv[1]);
char letters[size][5];
int total_combinations = 1;
int current_combination[size];
for (int i = 0; argv[1][i]; i++) {
switch (argv[1][i]) {
default:
fprintf(stderr, "Invalid input: \"%c\"\n", argv[1][i]);
return EXIT_FAILURE;
case '0':
strcpy(letters[i], "+");
break;
case '2':
strcpy(letters[i], "abc");
break;
case '3':
strcpy(letters[i], "def");
break;
case '4':
strcpy(letters[i], "ghi");
break;
case '5':
strcpy(letters[i], "jkl");
break;
case '6':
strcpy(letters[i], "mno");
break;
case '7':
strcpy(letters[i], "pqrs");
break;
case '8':
strcpy(letters[i], "tuv");
break;
case '9':
strcpy(letters[i], "wxyz");
break;
}
total_combinations *= strlen(letters[i]);
}
memset(current_combination, 0, sizeof current_combination);
char combinations[total_combinations][size + 1];
for (int k = 0; k < total_combinations; k++) {
for (int set_idx = 0; set_idx < size; set_idx++) {
int letter_idx = current_combination[set_idx];
combinations[k][set_idx] = letters[set_idx][letter_idx];
}
combinations[k][size] = '\0';
for (int i = size - 1; i >= 0; i--) {
current_combination[i]++;
if (current_combination[i] != strlen(letters[i]))
break;
current_combination[i] = 0;
}
}
char name[128];
char number[128];
while (1) {
if (!fgets(name, sizeof name, stdin))
break;
if (!fgets(number, sizeof number, stdin))
break;
int found = 0;
for (int i = 0; i < total_combinations; i++) {
if (strstr(name, combinations[i])) {
found = 1;
break;
}
}
if (found || strstr(number, argv[1]))
printf("%s, %s\n", remove_newline(name), remove_newline(number));
}
}
This program is about converting Roman number to decimal number. The program can convert the alphabet to number but it can not process the last roman digit. I think my flow is alright but the output is not right. Can any body give me a helping hand?
#include <stdint.h>
#include <stdio.h>
#include <string.h>
int roman_to_int(const char s[], int length) {
// Please complete the function body
int ans = 0, value[length];
for (int i = 0; i < length; i++) {
switch (s[i]) {
case 'I': value[i] = 1; break;
case 'V': value[i] = 5; break;
case 'X': value[i] = 10; break;
case 'L': value[i] = 50; break;
case 'C': value[i] = 100; break;
case 'D': value[i] = 500; break;
case 'M': value[i] = 1000; break;
}
}
for (int i = 0; i < length - 1; i++) {
if (value[i] >= value[i+1])
ans += value[i];
else {
ans = ans + value[i+1] - value[i];
i++;
}
}
return ans;
}
int main() {
char roman_num[] = "III";
char roman_num_2[] = "CXXIII";
char roman_num_3[] = "MMMCDLIX";
printf("roman_to_int(%s) = %d\n", roman_num,
roman_to_int(roman_num, strlen(roman_num)));
printf("roman_to_int(%s) = %d\n", roman_num_2,
roman_to_int(roman_num_2, strlen(roman_num_2)));
printf("roman_to_int(%s) = %d\n", roman_num_3,
roman_to_int(roman_num_3, strlen(roman_num_3)));
}
You should add the value of the last roman digit after the end of the second loop.
As an alternative, you could make value on entry longer than n and set the last entry to 0 so you won't need the make a special case of the last roman digit.
Note that you should also handle the case of unrecognised roman digits: either by ignoring them or by returning an error code, such as a negative value -1.
It is also simpler for roman_to_int to take a null terminated C string and compute the length there.
Here is a modified version:
#include <stdio.h>
#include <string.h>
int roman_to_int(const char s[]) {
// Please complete the function body
int length = strlen(s);
int ans = 0, value[length + 1];
for (int i = 0; i < length; i++) {
switch (s[i]) {
case 'I': value[i] = 1; break;
case 'V': value[i] = 5; break;
case 'X': value[i] = 10; break;
case 'L': value[i] = 50; break;
case 'C': value[i] = 100; break;
case 'D': value[i] = 500; break;
case 'M': value[i] = 1000; break;
default: return -1;
}
}
value[length] = 0;
for (int i = 0; i < length; i++) {
if (value[i] >= value[i + 1])
ans += value[i];
else
ans -= value[i];
}
return ans;
}
int main() {
char roman_num[] = "III";
char roman_num_2[] = "CXXIII";
char roman_num_3[] = "MMMCDLIX";
char roman_num_4[] = "MMMCDLIZ"; // error
printf("roman_to_int(%s) = %d\n", roman_num, roman_to_int(roman_num));
printf("roman_to_int(%s) = %d\n", roman_num_2, roman_to_int(roman_num_2));
printf("roman_to_int(%s) = %d\n", roman_num_3, roman_to_int(roman_num_3));
printf("roman_to_int(%s) = %d\n", roman_num_4, roman_to_int(roman_num_4));
return 0;
}
I have a task to print a list of words made up of "non-vowel, vowel, non-vowel", i.e bab, bac, bad, bad ... through to zuz.
I have managed to create a code which does the first two letters but gets lost on the last loop and prints only '}' - which seems strange to me. The code is below:
#include <stdio.h>
#include <string.h>
int check_vowel(char c);
int check_consonant(char c);
int main ()
{
char c, c2, c3;
int cnt;
for (cnt = 0; cnt <= c; cnt++)
{
for (c = 'a'; c <= 'z'; c++)
{
if (check_vowel(c) == 0)
{
for (c2 = 'a'; c2 <= 'z'; c2++)
{
if (check_consonant(c2) == 0)
{
for (c3 = 'a'; c3 <= 'z'; c3++);
{
if (check_vowel(c3) == 0)
{
cnt++;
printf("%d || %c%c%c\n", cnt, c, c2, c3);
}
}
}
}
}
}
}
printf("Total names = %d", cnt);
return 0;
}
int check_vowel(char c)
{
switch(c)
{
case 'a':
case 'e':
case 'i':
case 'o':
case 'u':
return 1;
default:
return 0;
}
}
int check_consonant(char c)
{
switch(c)
{
case 'b':
case 'c':
case 'd':
case 'f':
case 'g':
case 'h':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
return 1;
default:
return 0;
}
}
The outputs are along the lines of:
1 || ba}
2 || be}
etc
The first bug is in this line:
for (c3 = 'a'; c3 <= 'z'; c3++);
Because of the semicolon at the end of that line, the iteration body is "do nothing". The block of code after it (from { to }) is just treated as something to do after this loop. When you enter this block of code, c3 will always be {, because it's the first character after z (in most character encodings). The fix: remove the semicolon.
The second bug is in the choice of conditions. The condition check_vowel(c3) == 0 (what you wrote) is not equal to check_consonant(c3) == 1 (what you want). For instance, { is not a vowel, but that doesn't make it a consonant. The fix: make your conditions positive.
Why don't try to iterate only over the proper sets:
static const char cons[] = "bcdfghjklmnpqrstvwxyz";
static const size_t cons_sz = sizeof cons - 1;
static const char vowels[] = "aeiou";
static const size_t vowels_sz = sizeof vowels - 1;
for (i = 0; i < cons_sz; i++)
for (j = 0; j < vowels_sz; j++)
for (k = 0; k < cons_sz; k++)
printf("%c%c%c\n", cons[i], vowels[j], cons[k]);
I'm not sure how to make the multidimensional array able to perform the same task as the other three I am supposed to get rid of. Would I just simply replace all the places with the other arrays with just the one Multi? This is especially hard for me since I do not play cards and the logic behind the card game makes no sense to me.
DIRECTIONS
I need to remove the num_in_rank, num_in_suit, and card_exists arrays. Have the program store the cards in a 5 x 2 array instead. Each row of the array will represent a card. For example, if the array is named hand, then hand(0)(0) will store the rank of the first card and hand(0)(1) will store the suit of the first card.
/* Classifies a poker hand */
#include <stdbool.h> /* C99 only */
#include <stdio.h>
#include <stdlib.h>
#define NUM_RANKS 13
#define NUM_SUITS 4
#define NUM_CARDS 5
/* external variables */
int num_in_rank[NUM_RANKS];
int num_in_suit[NUM_SUITS];
bool straight, flush, four, three;
int pairs; /* can be 0, 1, or 2 */
/* prototypes */
void read_cards(void);
void analyze_hand(void);
void print_result(void);
/**********************************************************
* main: Calls read_cards, analyze_hand, and print_result *
* repeatedly. *
**********************************************************/
int main(void)
{
for (;;) {
read_cards();
analyze_hand();
print_result();
}
}
/**********************************************************
* read_cards: Reads the cards into the external *
* variables num_in_rank and num_in_suit; *
* checks for bad cards and duplicate cards. *
**********************************************************/
void read_cards(void)
{
bool card_exists[NUM_RANKS][NUM_SUITS];
char ch, rank_ch, suit_ch;
int rank, suit;
bool bad_card;
int cards_read = 0;
for (rank = 0; rank < NUM_RANKS; rank++) {
num_in_rank[rank] = 0;
for (suit = 0; suit < NUM_SUITS; suit++)
card_exists[rank][suit] = false;
}
for (suit = 0; suit < NUM_SUITS; suit++)
num_in_suit[suit] = 0;
while (cards_read < NUM_CARDS) {
bad_card = false;
printf("Enter a card: ");
rank_ch = getchar();
switch (rank_ch) {
case '0': exit(EXIT_SUCCESS);
case '2': rank = 0; break;
case '3': rank = 1; break;
case '4': rank = 2; break;
case '5': rank = 3; break;
case '6': rank = 4; break;
case '7': rank = 5; break;
case '8': rank = 6; break;
case '9': rank = 7; break;
case 't': case 'T': rank = 8; break;
case 'j': case 'J': rank = 9; break;
case 'q': case 'Q': rank = 10; break;
case 'k': case 'K': rank = 11; break;
case 'a': case 'A': rank = 12; break;
default: bad_card = true;
}
suit_ch = getchar();
switch (suit_ch) {
case 'c': case 'C': suit = 0; break;
case 'd': case 'D': suit = 1; break;
case 'h': case 'H': suit = 2; break;
case 's': case 'S': suit = 3; break;
default: bad_card = true;
}
while ((ch = getchar()) != '\n')
if (ch != ' ') bad_card = true;
if (bad_card)
printf("Bad card; ignored.\n");
else if (card_exists[rank][suit])
printf("Duplicate card; ignored.\n");
else {
num_in_rank[rank]++;
num_in_suit[suit]++;
card_exists[rank][suit] = true;
cards_read++;
}
}
}
/**********************************************************
* analyze_hand: Determines whether the hand contains a *
* straight, a flush, four-of-a-kind, *
* and/or three-of-a-kind; determines the *
* number of pairs; stores the results into *
* the external variables straight, flush, *
* four, three, and pairs. *
**********************************************************/
void analyze_hand(void)
{
int num_consec = 0;
int rank, suit;
straight = false;
flush = false;
four = false;
three = false;
pairs = 0;
/* check for flush */
for (suit = 0; suit < NUM_SUITS; suit++)
if (num_in_suit[suit] == NUM_CARDS)
flush = true;
/* check for straight */
rank = 0;
while (num_in_rank[rank] == 0) rank++;
for (; rank < NUM_RANKS && num_in_rank[rank] > 0; rank++)
num_consec++;
if (num_consec == NUM_CARDS) {
straight = true;
return;
}
/* check for 4-of-a-kind, 3-of-a-kind, and pairs */
for (rank = 0; rank < NUM_RANKS; rank++) {
if (num_in_rank[rank] == 4) four = true;
if (num_in_rank[rank] == 3) three = true;
if (num_in_rank[rank] == 2) pairs++;
}
}
/**********************************************************
* print_result: Prints the classification of the hand, *
* based on the values of the external *
* variables straight, flush, four, three, *
* and pairs. *
**********************************************************/
void print_result(void)
{
if (straight && flush) printf("Straight flush");
else if (four) printf("Four of a kind");
else if (three &&
pairs == 1) printf("Full house");
else if (flush) printf("Flush");
else if (straight) printf("Straight");
else if (three) printf("Three of a kind");
else if (pairs == 2) printf("Two pairs");
else if (pairs == 1) printf("Pair");
else printf("High card");
printf("\n\n");
}
to help you visualize it first define the multidimensional array, then use constants to access the indexes
ie.
//you could also use namespaces and enums or static class variables, const is simpler
const int RANK = 0; //or CARD_RANK if ambiguous/conflicting
const int SUIT = 1; //or CARD_SUIT if ambiguous
int cards[5][2]; //first index is card, second index is data about the card
//access first card's suit
int temp = cards[0][SUIT];
Assuming the program runs already, and all you need to do is this, then just do it and everything will work. The game seems to be poker or a variation btw.
I have written this code so far, and now i am required to use the frequency analysis to crack the code which I am not not clear of.
From what I understand, I must first count the frequency of letters in a string, then I would say compare it with the most frequent letters in German language, and later sort it with bubble sort. Is this correct?
I would really appreciate it if anyone could give me Ideas or hints on where to start. Thank you in advance.
EDITED: Hi Guys, i just edited my code and the frequency analysis seems to be working fine right now. It would help me if you guys can give comments or critics on my code. Thanks!
BTW its German language, i changed it.
#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <ctype.h>
#define MAX 100
struct Data
{
char letter;
int num;
};
typedef struct Data DATA;
void encode(char message[], int shift)
{
int i;
FILE *pout;
pout = fopen("Output_encode.txt", "w");
if (pout == NULL)
{
printf("File could not be opened for writing!\n");
exit(1);
}
for(i=0;i<strlen(message);i++)
{
if (!isalpha(message[i]))
continue;
// checking for upper case
if(isupper(message[i]))
message[i]=((message[i]-'A') + shift) % 26 + 'A';
else
//checking for lower case
if(islower(message[i]))
message[i]=((message[i]-'a') + shift) % 26 + 'a';
}
printf("\n\tEncoded text: %s\n", message);
fprintf(pout, "%s\n", message);
if (fclose(pout) != 0)
printf("Error in closing file!\n");
}
void decode(char message[], int shift)
{
int i;
FILE *pout;
pout = fopen("Output_decode.txt", "w");
if (pout == NULL)
{
printf("File could not be opened for writing!\n");
exit(1);
}
for(i=0;i<strlen(message);i++)
{
if (!isalpha(message[i]))
continue;
// checking for upper case
if(isupper(message[i]))
message[i]=((message[i]-'A') + (26-shift)) % 26 + 'A';
else
//checking for lower case
if(islower(message[i]))
message[i]=((message[i]-'a') + (26-shift)) % 26 + 'a';
}
printf("\n\tDecoded text: %s\n", message);
fprintf(pout, "%s\n", message);
if (fclose(pout) != 0)
printf("Error in closing file!\n");
}
void textfile_decode()
{
FILE *pin, *pout;
char filename_in[MAX], filename_out[MAX];
char text[MAX];
char text3[MAX]={0};
char table[26] = {'e','n','i','r','s','t','a','d','h','u','l','c','g','o','m','b','f','w','k','z','p','v','j','y','x','q'}; //Frequency letters in German dictionary
DATA temptext, text2[26];
int temp, position;
int i, m, max, trial, l=0, n=0, k=0;
printf("Enter name of input file: ");
scanf("%s", filename_in);
pin = fopen(filename_in, "r");
if (pin == NULL)
{
printf("File could not be opened for reading!");
}
printf("Enter name of output file: ");
scanf("%s", filename_out);
pout = fopen(filename_out, "w");
if (pout == NULL)
{
printf("File could not be opened for writing!");
}
printf("\nOriginal Code:\n");
while(!feof(pin))
{
fgets(text, MAX, pin); //Read from textfile
fputs(text, stdout); //Show original code on console
}
printf("\n");
if (pin == NULL)
{
printf("File could not be opened for reading!");
}
fclose(pin);
pin = fopen(filename_in, "r");
for (i = 0; i <= 25; i++)
{
text2[i].letter = 'a' + i; //Array elements A-Z
text2[i].num = 0; //Number of letters (Frequency)
}
while(!feof(pin))
{
i = 0;
fgets(text, MAX, pin); //Read from textfile per line
while(text[i] != '\0')
{
if(1 == isupper(text[i])) // Replace capital letters with small
{
text[i] += 32;
}
switch(text[i]) //Counting letters (letter frequency)
{
case 'a':
text2[0].num += 1;
break;
case 'b':
text2[1].num += 1;
break;
case 'c':
text2[2].num += 1;
break;
case 'd':
text2[3].num += 1;
break;
case 'e':
text2[4].num += 1;
break;
case 'f':
text2[5].num += 1;
break;
case 'g':
text2[6].num += 1;
break;
case 'h':
text2[7].num += 1;
break;
case 'i':
text2[8].num += 1;
break;
case 'j':
text2[9].num += 1;
break;
case 'k':
text2[10].num += 1;
break;
case 'l':
text2[11].num += 1;
break;
case 'm':
text2[12].num += 1;
break;
case 'n':
text2[13] .num+= 1;
break;
case 'o':
text2[14].num += 1;
break;
case 'p':
text2[15].num += 1;
break;
case 'q':
text2[16].num += 1;
break;
case 'r':
text2[17].num += 1;
break;
case 's':
text2[18].num += 1;
break;
case 't':
text2[19].num += 1;
break;
case 'u':
text2[20].num += 1;
break;
case 'v':
text2[21].num += 1;
break;
case 'w':
text2[22].num += 1;
break;
case 'x':
text2[23].num += 1;
break;
case 'y':
text2[24].num += 1;
break;
case 'z':
text2[25].num += 1;
break;
default: break;
}
i++;
}
}
for(i = 0; i <= 26; i++) // Sorting array text2 according to letter frequency
{
temp = text2[i].num;
for(m = i+1; m <= 27; m++)
{
if(text2[m].num > temp)
{
max = m;
temp = text2[m].num;
}
}
temptext = text2[max];
text2[max] = text2[i];
text2[i] = temptext;
}
fclose(pin);
fclose(pout);
pin = fopen(filename_in, "r");
pout = fopen(filename_out, "w");
do
{
k += 1;
} while (text2[k].num == text2[k+1].num); //Check--> How many letters have the same frequency
trial = 2;
while(!feof(pin))
{
fgets(text, MAX, pin);
do
{
position = table[l] - text2[n].letter; // determine letter position
i = 0;
do
{
if(0 !=isalpha(text[i]))
{
if(0 != isupper(text[i])) // Checking for uppercase
{
text3[i] = text[i];
text3[i] = text3[i] + position;
if(text3[i] > 90) // If exceeds Alphabets, start again from 'A'
{
text3[i] = text3[i] - 26;
}
else if (text3[i] < 65)
{
text3[i] += 26;
}
}
else if (0 != islower(text[i])) // checking for lowercase
{
text3[i] = text[i];
text3[i] = text3[i] + position;
if(text3[i] > 122) // If exceeds Alphabets, start again from 'a'
{
text3[i] = text3[i] - 26;
}
else if(text3[i] < 97)
{
text3[i] += 26;
}
}
}
else
{
text3[i] = text[i]; // All other non letters are simply replaced
}
i++;
}while(text[i] != '\0' );
if (trial== 2)
{
printf("\n");
fputs(text3, stdout);
printf("\nCode decrypted? (0)NO (1)YES : ");
scanf("%d", &trial);
printf("\n");
}
if (trial == 0 && n != k) // Code not decrypted, letters have different frequency
{
n++;
trial = 2;
}
if (trial == 0 && n == k) // Code not decrypted, letters have same frequency
{
l++;
n = 0;
trial = 2;
}
if (trial == 3) // First line of code is decrypted, following lines will decrypted using same shift position
{
trial = 1;
}
}while(trial != 1);
fputs(text3, stdout); //Show on console window
fputs(text3, pout);
memset(text3,'\0',100); // Reset text3 array
memset(text,'\0',100); // Reset text array
trial = 3; // First line of code decrypted, shift position is saved
}
fclose(pin);
fclose(pout);
}
int main()
{
int shift, choice1, choice2;
char message[MAX];
do{
printf("Selection: \n");
printf("(1) Encode/Decode\n");
printf("(2) Decode Textfile\n");
printf("(3) End Programme\n");
printf("User input: ");
scanf("%d", &choice1);
fflush(stdin);
switch(choice1){
case 1:
printf("\nEnter message to be encrypted: ");
gets(message);
printf("Enter shift amount (1-25): ");
scanf("%d", &shift);
printf("\nSelection: \n");
printf("(1) Encode\n");
printf("(2) Decode\n");
printf("User input: ");
scanf("%d", &choice2);
switch(choice2)
{
case 1:
encode(message, shift);
break;
case 2:
decode(message, shift);
break;
}
break;
case 2:
textfile_decode();
break;
}
printf("\n\n");
}while(choice1!=3);
printf("\n");
return 0;
}
A Caesar ciphre changes characters by shifting them n places.
There are two very simple approaches to solving a shift ciphre:
Print all 25 possible solutions. Manually select the one that contains readable text.
Get the frequency of the characters (not the words). Then perform the shift that best aligns with a frequency table of the language the message was written in (English in your case?).
To break the code you can use 3 different approaches:
The first one is what you cited: count the frequency of words in a text ( I would rather use a Map for that, using the string as key and rising the number of hits as value.), and guessing the letters by comparing it to the frequency of words used in normal texts.
The second solution would be to do same with letters and guessing the meaning by comparing your frequency with the frequency of letters in a normal text.
The third solution would be to take single words of the text and trying all possible shiftings of the letters until you get words that mean something.
Here you can find some good sources!