Related
I’m a novice in C and I’m practicing with some code in order to improve my knoowledge.
With the following code I’m trying to encrypt a given text returning, for every text character, a character that is the result of moving in the alphabet n positions forward.
So, for example, for the given text “hello” and the key 6 (so for every char of ‘hello‘ shifting of 6 positions in the alphabet chars array) the encrypted text will be “nkrru“.
My code use:
empty char array strResult where to store the resulting encrypted text;
temporary char array tmp where to store the shifted chars.
I also used two nested loop: in the first one I iterate, char by char, into the given text string and in the inner one I iterate into the alphabet chars in order to find the equivalent character. Then I populate the tmp array with the alphabet chars shifted by key times.
Meanwhile I manage the case if the shifting forward goes over the 26 letters of the alphabet and if one of the char is a white space.
At the end, using strcat() I concatenate tmp to strResult and print it.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main()
{
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
int key = 9;
string word = "Hello World";//text to encrypt
char resultStr[strlen(word)];//string where to store the resulting encrypted text
char tmp[strlen(word)];//temporary string
int counter;//for the shifted index
for (int i = 0, n = strlen(word); i < n; i++)
{
char wordcurrChar = tolower(word[i]);//convert every wordcurrChar to lowercase
int checkSpace = isspace(wordcurrChar);//check if the character is a white space
//inner loop to iterate trough alphabet array
for (int j = 0, m = strlen(ALPHABET); j < m; j++)
{
counter = j+key;//counter has now the value of the shifted index
char ALPHcurrChar = ALPHABET[j];
if ((wordcurrChar == ALPHcurrChar) && (counter <= 26)) {//if counter is whitin the 26 characters of the alphabet
tmp[i] = ALPHABET[counter];//then shift at index counter
} else if ((wordcurrChar == ALPHcurrChar) && (counter > 26)) {//if counter is over the 26 characters of the alphabet
int newCounter = counter - 26;//then, once over Z, start from the beginning of the alphabet
tmp[i] = ALPHABET[newCounter];
} else if (checkSpace != 0) {//if the character is a white space
tmp[i] = ' ';//then return a white space also for the encrypted text
}
}
}
strcat(resultStr, tmp);//concat resultStr and temporary string
printf("%s\n", resultStr);//print the encrypted text
}
Everithing looks to work fine, except from the fact that when the shifting goes over the 26 alphabet chars and has to return as encrypted char the specific letter 'A' it stops.
So, for example, if I give as string to encrypt hello world with key=9, result is qnuux fx_ _ _. The last 3 letters are missing. Basically, the shifting of char r in hello world (that shifted of 9 position in the alphabet array goes over the 26th position and end in in char a position) stops everything.
Again, this happen only if the shifting goes over the 26 letters of the alphabet and end on the specific position of char a (with all other cases works fine). So with hello world and key = 15, the result will be wt_ _ _ _ _ _ _ _ stopping at the shifting of the first l of hello world. This because the shifting goes over the 26 alphabet letters and reach char a position.
I spent hours and hours to understand why is happening but with no success.
Hope someone can help.
Thanks a lot in advance.
P.s: I tried many different changing of the if statment that manage the condition of going over the 26 alphabet array positions but nothing really helped.
Code seems to have missed the idea that with the C library, a string has a '\0'
A string is a contiguous sequence of characters terminated by and including the first null character. C17dr 7.1.1 1
At least these problems:
strlen() on a non-string
strlen(ALPHABET) leads to undefined behavior (UB) as strlen() expects a string and ALPHABET is not a string as it lacks a terminating null character.
// Bad
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
for (int j = 0, m = strlen(ALPHABET); j < m; j++)
// Add null character
char ALPHABET[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '\0'};
// or simply
char ALPHABET[] = "abcde...wxyz";;
strcat() with a non-string
Similarly, strcat() below fails as strcat() expects resultStr and tmp to be strings. Neither are strings as they lack a terminating null character.
char resultStr[strlen(word)];
char tmp[strlen(word)];
....
// Bad
strcat(resultStr, tmp);
Alternate code would increase array size by 1 and add the final '\0'.
Not-productive to repeated walk the word to find its length.
size_t word_length = strlen(word);
char resultStr[word_length + 1u];
char resultStr[0] = '\0';
char tmp[word_length + 1u];
....
char tmp[word_length] = '\0';
strcat(resultStr, tmp);
You can simply initialise a char array with a string.
char ALPHABET[] = "abcdefghijklmnopqrstuvwxyz";
It's better to #define magic numbers
#define CIPHER_ROTATIONS 9
Every cipher should be recoverable, for data consistency. So, ignoring letter-case is loss of info.
char wordcurrChar = tolower(word[i]);
Simplified code:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define CIPHER_ROTATIONS 27
// shift can be positive(encoding) or negative(decoding)
void caeser_cipher (const char* input, const int tlen, int shift, char* output)
{
shift %= 26; // more than 26 make cycle[s]
if (shift < 0) shift += 26; // moving x steps backward = moving (26-x) forward
for (int ti=0; ti < tlen; ++ti) {
if (islower (input[ti]))
output[ti] = 'a' + (input[ti] - 'a' + shift) % 26;
else if (isupper (input[ti]))
output[ti] = 'A' + (input[ti] - 'A' + shift) % 26;
else
output[ti] = input[ti];
}
}
int main() {
char text[] = "Caesar cipher, also known as Caesar's cipher, the shift cipher, Caesar's code or Caesar shift";
int tlen = strlen(text);
char cipher[tlen +1]; cipher[tlen] ='\0';
int shift = CIPHER_ROTATIONS; // also called rotations
printf ("Text :\t\t[%s]\n", text);
caeser_cipher (text, tlen, shift, cipher);
printf ("Cipher:\t\t[%s]\n", cipher);
text[0] = '\0';
caeser_cipher(cipher, tlen, -shift, text);
printf ("DecipheredText: [%s]\n", text);
return 0;
}
I am new to C, and I am learning shift operation.
I understand the shift operation if the data is a binary number, but for my code in this case, I want to implement the case that 't', 'h', 'i', 's', ' ', '\0' are all discarded or shifted, and move 'a' to the first element of this string.
Can I do this behavior by using shift operator? If so, why is so?
Really appreciated.
char words[10] = {'t', 'h', 'i', 's', ' ', '\0', 'a', 'b', 'c'};
The shift operator you are talking about is basically bitwise operator. You can't use this to shift array of characters.
To accomplish what you asked, you can write a function. Suppose you want left shift -
int leftShift(char *words, int len)
{
int i;
for(i = 1; i < len; i++)
{
words[i - 1] = words[i];
}
len--;
return len;
}
What this function does? - it takes an array and length of that array as parameter, and perform left shift one time.
So then from your main function you can just call this method any number of times you want -
int main(void) {
char words[10] = {'t', 'h', 'i', 's', ' ', '\0', 'a', 'b', 'c'};
int len = 10;
len = leftShift(words, len); // left shift one time - this will discard 't'
len = leftShift(words, len); // left shift one time - this will discard 'h'
//finally print upto len, because len variable holds the new length after discarding two characters.
int i;
for(i = 0; i < len; i++)
{
printf("'%c', ", words[i]);
}
return 0;
}
This is very trivial idea, surely this approach can be improved in many ways. But I think you got the basic idea.
I have seen similar answers on stack overflow, but none had the same problem as I do. My problem with this simple caesar cipher encryption function is that for the position [2] of my encrypted array (and only at position[2]) a (^3) symbol, followed by the encrypted string at position [3] gets concatenated to it.
void encryptIDS(char login_ids[5][6])
{
int i = 0;
int j = 0;
int stop = 0;
int alphabet_count = 0;
int numbers_count = 0;
char lower_case[27] = {'a','b','c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
char numbers[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9'};
char encrypted_string[5][6];
for (j = 0; j < 5; j++)
{
for (i = 0; i < 5; i++)
{
alphabet_count = 0;
numbers_count = 0;
stop = 0;
while (stop == 0)
{
if (login_ids[j][i] == lower_case[alphabet_count])
{
encrypted_string[j][i] = ((((login_ids[j][i]-'a')+7)%26)+'a');
stop = 1;
}
else if (login_ids[j][i] == numbers[numbers_count])
{
encrypted_string[j][i] = ((((login_ids[j][i] - '0')+7)%10)+'0');
stop = 1;
}
else
{
alphabet_count++;
numbers_count++;
}
}
}
}
printf("debug : %s\n", encrypted_string[0]);
printf("debug : %s\n", encrypted_string[1]);
printf("debug : %s\n", encrypted_string[2]);
printf("debug : %s\n", encrypted_string[3]);
printf("debug : %s", encrypted_string[4]);
}
the content of login_ids is "vc136","jc580","cl274","tm361,"ns792"
when printing them out they all encrypt nicely except for the troublesome 3rd one as "cj803", "qj257", "js941^3at038", "at038", "uz469"
this is quite mistifying as the string at position [2] has more than 6 characters, which it shouldnt have.
Any and all feedback is appreciated, including comments on how awful my code is, or if this question is misusing the website in any way. Thank you.
Don't be offended, but your code is awful as you suspected :)
But, this is an opportunity to learn why, and how you can fix it, so I will explain int detail:
You code has 4 main problems.
Two of them break the program and will cause it to print garbage or crash with segmentation fault, depending on the memory layout when your program runs.
The other two let your program print the correct answer, but make it horribly inefficient by wasting memory and processor cycles.
If this is an assignment you are being graded on, you will loose score, and if you are learning for yourself, this is a bad programming practice.
Problem 1:
As already pointed out in the comments, you must always NULL terminate your strings in C.
If you don't, functions like printf that use them will keep printing memory content until they encounter a random NULL value or until your program crashes.
So, in your outer for loop, you must add a line like this:
for (j = 0; j < 5; i++) {
//code to do the cypher
encrypted_string[j][5] = '\0'; //add terminator to string
}
Alternatively, you may initialize the array the way Bwebb shown you:
encrypted_string[5][6] = { '\0' };
This will put zeros in all array cells in advance. But, if you use this method, be careful not to overwrite the last cell of a string in one of your loops!
Problem 2:
Also pointed out by Bwebb, you increment alphabet_count and numbers_count together, but use numbers_count to index a smaller array.
This means for any letter from k to z your program will access memory it should not touch when it performs the second if check.
This problem will go away on its own however, if you fix the following two problems and structure your code correctly.
Problem 3:
You waste memory with two big arrays: the letter array and the number array.
You don't need them! Since you calculate the cypher correctly, you must already know how letters and numbers are represented in a computer, so you don't need to compare your string to the contents of these arrays one by one.
You know letters are a continuous range, so just use a single if.
Problem 4:
You waste time with the while loop.
This is part of the same issue stated in problem 3 - you don't need to loop!
Just make one comparison to see if the current character is in the range of letters and if it is not, make another comparison to see if it is in the range of numbers.
Your code will work correctly if you fix only problems 1 and 2, but if you fix 3 and 4 your code will be shorter and easier to read and understand, so there will be much less chance of any other errors sneaking in.
Here is how to do this:
//this is the inner loop, the outer loop over j stays the same
for (i = 0; i < 5; i++) {
//check if this is a letter - a range between a and z inclusive:
if (encrypted_string[j][i] >= 'a' && encrypted_string[j][i] <= 'z') {
encrypted_string[j][i] += 7; //encrypt through shift by 7
//rotate to beginning of alphabet if needed:
if (encrypted_string[j][i] > 'z') encrypted_string[j][i] -= 26;
} else if (/* do the same thing for numbers) {
...
}
}
Note, that I split your encryption calculation in to two lines: this is also to make it easy to read and understand.
You can still use your original calculation:
encrypted_string[j][i] = ((((login_ids[j][i] - 'a') + 7) % 26) + 'a');
But readability is an important aspect of any good code.
#include <stdio.h>
void encryptIDS(char login_ids[5][6]);
int main(void) {
char login_ids[5][6] = {{"vc136"},{"jc580"},{"cl274"},{"tm361"},{"ns792"}};
encryptIDS(login_ids);
return 0;
}
void encryptIDS(char login_ids[5][6])
{
int i = 0;
int j = 0;
int stop = 0;
int alphabet_count = 0;
int numbers_count = 0;
char lower_case[27] = {'a','b','c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
char numbers[10] = {'0', '1', '2', '3', '4', '5', '6', '7', '8',
'9'};
char encrypted_string[5][6] = {0};
for (j = 0; j < 5; j++)
{
for (i = 0; i < 5; i++)
{
alphabet_count = 0;
numbers_count = 0;
stop = 0;
while (stop == 0)
{
if (login_ids[j][i] == lower_case[alphabet_count])
{
encrypted_string[j][i] = ((((login_ids[j][i]-'a')+7)%26)+'a');
stop = 1;
}
if(numbers_count < 10)
{
if (login_ids[j][i] == numbers[numbers_count])
{
encrypted_string[j][i] = ((((login_ids[j][i] - '0')+7)%10)+'0');
stop = 1;
}
}
alphabet_count++;
numbers_count++;
}
}
}
printf("debug : %s\n", encrypted_string[0]);
printf("debug : %s\n", encrypted_string[1]);
printf("debug : %s\n", encrypted_string[2]);
printf("debug : %s\n", encrypted_string[3]);
printf("debug : %s", encrypted_string[4]);
}
OUTPUTS
debug : cj803
debug : qj257
debug : js941
debug : at038
debug : uz469
The issue was with the index into numbers, when the letter took more than 10 iterations to find a match. I re-arranged the while loop logic to prevent the out of bounds index, is this the result you were expecting?
EDIT I copy/pasted your code into codechef.com/ide to fix it, but i didnt fix all of the formatting/indenting. You should fix the formatting on your own prior to posting next time (just an FYI for the people who answer you).
I was faced with a question recently in C. We have a phone's numpad with following layout:
1[abc] 2[def] 3[ghi]
4[jkl] 5[mno] 6[pqr]
7[st] 8[uv] 9[wx]
0[yz]
How to come up with an API which gives all possible combinations of characters belonging to each number for a given numeral input.
For e.g. input = 1234
Then the API should print all possible combinations of characters-
adgj bdgj cdgj aegj begj cegj.. and so on.
Is there a simple way to do it? Apart from hardcoded nested for loops.
I was given a hint as recursion but couldn't figure a way out of it.
Recursion is a good solution for such problems, where you must find combinations. The advantage over nested loops is that recursion works for strings of any length.
In your case, you need a function that takes:
the original string
an auxiliary char buffer for the solution* and
the current index, which starts at 0.
Recursive functions require a termination condition: When you have reached the end of the original string, print it and return.
Otherwise, take the next digit, check whether it is valid, determine the letters associated with it and then call the function for each of the letters. That is, for each letter, copy it to the solution at the current index, then call the function with the next index.
Below's an example implementation that uses an intermediate function to do some house-keeping:
#include <stdlib.h>
#include <stdio.h>
/*
* Recursive back-end, that produces all combinations in sol.
*/
void alpha_r(const char *str, char *sol, int index)
{
const char *combo[] = {
"yz", "abc", "def", "ghi", "jkl", "mno", "pqr", "st", "uv", "wx"
};
if (str[index] == '\0') {
printf("%s\n", sol);
} else {
int k = str[index] - '0';
const char *p = combo[k];
while (*p) {
sol[index] = *p++;
alpha_r(str, sol, index + 1);
}
}
}
/*
* Non-recursive front-end that checks the string for validity
* and creates a temporary buffer for the solutions.
*/
void alpha(const char *str)
{
int len = 0;
while (str[len]) {
if (str[len] < 0 || str[len] > '9') {
fprintf(stderr, "Invalid input.\n");
return;
}
len++;
}
char sol[len + 1];
sol[len] = '\0';
alpha_r(str, sol, 0);
}
int main()
{
alpha("123");
return 0;
}
*) You could also use the string itself to store the solutions.
(That is not the standard layout for a phone, by the way.)
The tricky part is handling the data structures. It is handy that the input string consists of numbers, because then we can use the digits in the string to index an array that holds the possible letters for each number.
The idea is to modify an output string at a particular index using a for loop to go over all the possible replacements at that index. Then recursively move to the next index in the output array in the body of the for loop.
If you reach the end of the array, then print and return.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char* data[] = {"0", "1", "2abc", "3def", "4ghi", "5jkl", "6mno", "7prs", "8tuv", "9wxy"};
char* input = "23456783";
char* arr;
void current(int index)
{
if(index == strlen(input)) printf("%s\n", arr);
else
{
for(int i = 0; i < strlen(data[input[index] - '0']); ++i)
{
arr[index] = data[input[index] - '0'][i];
current(index + 1);
}
}
}
void main()
{
arr = malloc(strlen(input) + 1);
arr[strlen(input)] = '\0';
printf("%s\n\n", input);
current(0);
}
A way to find the combinations that you are looking for could be bitwise logic, with a binary number and an integer. The binary number would be as long as the string, with 0's and 1's acting as on and off switches for what is included and excluded in the string. The thing here is that we use base 3 or 4 depending on the number "pressed", and
If base four, then some if statements have to be applied to move the ones along that are actually base three.
Recursion is just a sneaky way of nesting four for loops. Here's what the code looks like
#include <stdio.h>
void sneaky( int depth, int maxDepth, char str[] )
{
char c, start;
start = 'a' + depth * 3;
for ( c = start; c < start + 3; c++ )
{
str[depth] = c;
str[depth+1] = '\0';
if ( depth == maxDepth )
printf( "%s\n", str );
else
sneaky( depth + 1, maxDepth, str );
}
}
int main( void )
{
char str[5] = { 0 };
sneaky( 0, 3, str );
}
You can also solve this problem, and similar combinatorial problems, with a simple counting algorithm. A counting algorithm emulates natural counting, in which you increment the least significant digit from 0 to 9. When the least significant digit wraps from 9 back to 0, the next digit to the left is incremented.
The same can be done to solve the OP's problem. But in this case, the digits have either two or three possible values. And if you examine the pattern in the OP, it's readily apparent that the least significant digit is on the left. In the pattern
adgj bdgj cdgj aegj
you can see that a becomes b, b becomes c, and when c wraps back to a, then d becomes e.
Here's the code
#include <stdio.h>
#include <stdlib.h>
static char InitialValue[] = { 'y', 'a', 'd', 'g', 'j', 'm', 'p', 's', 'u', 'w' };
static char NextValue[] = { 'b', 'c', 'a', 'e', 'f', 'd', 'h', 'i', 'g',
'k', 'l', 'j', 'n', 'o', 'm', 'q', 'r', 'p',
't', 's', 'v', 'u', 'x', 'w', 'z', 'y' };
static void error( char *msg )
{
fprintf( stderr, "%s\n", msg );
exit( EXIT_FAILURE );
}
int main( void )
{
int i, oldDigit;
char str[12];
// get the input string from the user
printf( "Enter the input string: " );
fflush( stdout );
if ( scanf( "%10s", str ) != 1 )
error( "whatever" );
// convert the input string to the corresponding first output string
for ( i = 0; str[i] != '\0'; i++ )
{
if ( str[i] < '0' || str[i] > '9' )
error( "invalid input string" );
str[i] = InitialValue[str[i] - '0'];
}
printf( "%s\n", str );
// use a simple counting algorithm to generate the string combinations
for (;;)
{
for ( i = 0; str[i] != '\0'; i++ )
{
oldDigit = str[i]; // save the current digit
str[i] = NextValue[oldDigit - 'a']; // advance the digit to the next value
if ( str[i] > oldDigit ) // if the digit did not wrap
break; // then we've got a new string
}
if ( str[i] == '\0' ) // if all the digits wrapped
break; // then we're done
printf( "%s\n", str ); // output the new string
}
return( EXIT_SUCCESS );
}
#include <stdio.h>
#include <string.h>
void main()
{
char alfavita[30] =
{
'a',
'b',
'c',
'd',
'e',
'f',
'g',
'h',
'i',
'j',
'k',
'l',
'm',
'n',
'o',
'p',
'q',
'r',
's',
't',
'u',
'v',
'w',
'x',
'y',
'z'
};
char str[20];
printf("Give a word:\n");
gets(str);
for(int i=0;i<strlen(str);i++)
{
for(int j=0;j<strlen(alfavita);j++)
if(alfavita[j] == str[i])
str[i] = alfavita[j+3];
}
puts(str);
}
For example if i give 'a' it should be return 'd' (each letter will transform into the 3d next of the alfavita array ) But it just prints me a null string. I can't find something wrong or I don't see it .
str[i] = alfavita[j+3];
After this line the code continues, so it will put i+3, i+6, ... until it gets out of alfavita.
You can add a break to exit the inner loop like that:
for(int i=0;i<strlen(str);i++)
{
for(int j=0;j<strlen(alfavita);j++)
if(alfavita[j] == str[i])
{
str[i] = alfavita[j+3];
break; // next i.
}
}
, or maybe just directly access the array:
for(int i=0;i<strlen(str);i++)
{
char c = str[i];
if (c >= 'a' && c <= 'z') {
str[i] = alfavita[(c - 'a' + 3) % strlen(alfavita)];
}
}
Note the % strlen(alfavita) to avoid ending after the end of the list.
You could also write it:
if (c >= 'a' && c <= 'z') {
str[i] = ((c - 'a' + 3) % 26) + 'a';
}
You can use a table that gives the replacement character for each character.
Then encode by computing the index into plain, and transferring that index into encoded:
char encode_char(char c)
{
const char *plain = "abcdefghijklmnopqrstuvwxyz";
const char *encoded = "defghijklmnopqrstuvwxyzabc";
const char *pp = strchr(plain, c);
if(pp != NULL)
return encoded[(ptrdiff_t) (pp - plain)];
return '?';
}
How the above works:
Define two strings that are supposed to be 1:1 mapped, i.e. plain[0] is encoded into encoded[0]. This can be more clearly modelled (i.e. by a struct that has the pair) but then the iniialization becomes more complicated.
Search for the input character c inside the plain string. This returns NULL if not found, or a pointer to somewhere inside plain found.
Make sure the pointer isn't NULL before using its value.
Subtract plain (i.e. &plain[0], the address of the a) from pp. This evaluates to 0 for a, 1 for b, and so on.
Use the computed index to look up the corresponding character in encoded.
On failure to encode, return ?.
In a portable, general program, you can not use plain subtraction (i.e. c - 'a'), since C does not guarantee that characters are encoded in values next to each other.
As pointed out, the above assumes that each character encodes in exactly one char. That might not be true for targets with exotic encodings, in which case it really is safer to use an explicit table, like this:
const struct {
char plain;
char encoded;
} encoding[] = {
{ 'a', 'd' },
{ 'b', 'e' },
{ 'c', 'f' },
/* ... and so on ... */
};
The encoding function then becomes:
char encode_char2(char c)
{
for(size_t i = 0; i < sizeof encoding / sizeof *encoding; ++i)
{
if(encoding[i].plain == c)
return encoding[i].encoded;
}
return '?'; /* Not found. */
}