First, don't use gets()
it is so vulnerable to exploit by buffer overrun is has been completely removed from the current C standard library, see Why gets() is so dangerous it should never be used!
While there is nothing wrong with reading and buffering your input (using fgets()
or POSIX getline()
), there is no real need to do so if you simply want to output the encrypted input. You can use getchar()
to read each character of input and simply convert each character as they are read.
Regardless whether you buffer the input of just convert it on the fly, organizing your +13
(or if previous char was a vowel +14
) logic into a simple function that takes the current character c
and the previous character prev
can help keep your logic straight and code clean, e.g.
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
(note: the validation of the input with islower()
from the ctype.h
header ensures your conversion is applied to only lowercase characters. You can expand it to handle both cases -- that is left to you)
Also note the logic of the addition wraps back to the beginning in case adding +13
(or +14
) would result in a character beyond 'z'
. (for example 'z' + 13 == 'm'
). You can adjust as required.
Then your code becomes simply:
int main (void) {
int c, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
while ((c = getchar()) != '\n' && c != EOF) { /* read each char */
putchar (encrypt (c, prev)); /* output encrypted */
prev = c; /* save current as prev */
}
putchar ('\n'); /* tidy up with newline */
}
Putting it altogether in a short example and adding the two required header files, you could do:
#include <stdio.h>
#include <ctype.h>
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
int main (void) {
int c, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
while ((c = getchar()) != '\n' && c != EOF) { /* read each char */
putchar (encrypt (c, prev)); /* output encrypted */
prev = c; /* save current as prev */
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
$ ./bin/charencrypt
Enter a message to encrypt: hello
urzyb
Look things over and let me know if you have further questions.
Edit - Converting an Array of Chars
To read into a buffer (array) of char
and then convert each character in the buffer based on the same logic, requires little change. The only change required is instead of converting each character on-the-fly, you read your input into a buffer, then loop over each character in the buffer making the conversions. (only caveat, you must save the prev/last char before making the conversion so you know how to correctly apply the next conversion)
The changes needed to the above are minimal. The follow uses the exact same encrypt
function and a single character array to store what is read from the user and then the conversions are made in-place updating the characters in the same array, e.g.
#include <stdio.h>
#include <ctype.h>
#define MAXC 2048 /* don't skimp on buffer size (except for embedded dev.) */
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
int main (void) {
char buf[MAXC], *p = buf; /* buffer and pointer to buffer */
int current, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
while (*p && *p != '\n') { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
fputs (buf, stdout); /* output converted buffer */
}
How you loop over the characters in the array is up to you. You can use a for
loop with array indexes, or simply use a pointer and advance the pointer to the next character on each iteration until you reach the nul-terminating character or '\n'
character, each of which would signify the end of the characters you are converting.
If you want to use a second array so that you preserve both the original and have the new encrypted array, just declare another array and fill it in the same manner, except instead of writing the converted values back to the original array, write it to your second array (don't forget to nul-terminate the second array)
Example Use/Output
The out is exactly the same:
$ ./bin/charencryptbuf
Enter a message to encrypt: hello
urzyb
Let me know if you have further questions.
Edit Based On Comment To Encrypt Multiple-Words
If you simply want to prompt the user for the number of words to encrypt, that as in my comment, you just need to wrap what you are doing in a loop. Prompt the user for the number of words to encrypt, then loop that number of times encrypting each word. The changes to the code above are minimal, e.g.
int main (void) {
char buf[MAXC]; /* buffer to hold input */
int nwords; /* no. of words to encrypt */
fputs ("How may words to encrypt?: ", stdout); /* prompt no. of words */
if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &nwords) != 1 ||
nwords < 1) {
fputs ("error: invalid integer input or nothing to do.\n", stderr);
return 1;
}
for (int i = 0; i < nwords; i++) {
char *p = buf; /* pointer to buf */
int current, prev = 0; /* current and prev chars */
printf ("\nenter word[%d]: ", i + 1); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
while (*p && *p != '\n') { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
fputs (buf, stdout); /* output converted buffer */
}
}
Example Use/Output
$ ./bin/charencryptbufmulti
How may words to encrypt?: 6
enter word[1]: hello
urzyb
enter word[2]: there
gurfr
enter word[3]: how
ubk
enter word[4]: are
nfr
enter word[5]: you
lbi
enter word[6]: hello
urzyb
Separating And Encrypting Any Number of Words Entered by the User Individually
To encrypt multiple words separately, you just need to do what you are doing for the whole string but separating the input into tokens (individual words). C provides the strtok()
function to do just that, but you will want to make a copy of the entire input string if you need to preserve it as strtok()
modifies the original. You can also simply make a copy of each token to preserve the original word and encrypted word separately.
An easy way to implement the addition is just to write a small wrapper-function that takes whole words as an input parameter and then passes the word to the existing encrypt()
function. So for the encrypt-word (or encrypt-wrapper), you could do:
/** Simple wrapper function that takes a word and passes it to encrypt */
char *encryptw (char *buf)
{
char *p = buf; /* pointer to buffer holding word */
int current, prev = 0; /* current and previous characters */
while (*p) { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
return buf;
}
(note: the char*
type and return buf;
is just a convenience to allow you make immediate use of the encrypted word, e.g. char word[] = "hello"; puts (encryptw (word));
Also note, encryptw()
modifies the input, so you could not pass a string-literal, e.g. encryptw("hello");
)
Having moved the code that encrypts a word into the encryptw()
function, all you need to do in main()
is separate the words into tokens and pass each token to encryptw()
to encrypt and then output the results. You must include string.h
for strtok()
as well as for strcpy()
, e.g.
#include <string.h>
...
#define MAXW 128 /* max individual word size to encrypt */
#define DELIM " \t\n" /* strtok delimiters */
...
int main (void) {
...
/* loop separating buf into individual words to encrypt */
p = strtok (buf, DELIM); /* 1st call - pass buf */
while (p) { /* validate return not NULL */
strcpy (word, p); /* make copy, to preserve original */
encryptw (word); /* pass word to encryptw to encrypt word */
/* output word, original and encrypted */
printf ("word[%2zu] : %-12s : (%s)\n", ++n, p, word);
p = strtok (NULL, DELIM); /* all subsequent calls - pass NULL */
}
}
(note: above the output is now the word number encrypted, e.g. word[1]..
followed by the original word and then the encrypted word in parenthesis)
The full code containing all changes would be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 2048 /* don't skimp on buffer size (except for embedded dev.) */
#define MAXW 128 /* max individual word size to encrypt */
#define DELIM " \t\n" /* strtok delimiters */
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
/** Simple wrapper function that takes a word and passes it to encrypt */
char *encryptw (char *buf)
{
char *p = buf; /* pointer to buffer holding word */
int current, prev = 0; /* current and previous characters */
while (*p) { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
return buf;
}
int main (void) {
char buf[MAXC], *p = buf, /* buffer and pointer to buffer */
word[MAXW]; /* array for word to encrypt */
size_t n = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
putchar ('\n');
/* loop separating buf into individual words to encrypt */
p = strtok (buf, DELIM); /* 1st call - pass buf */
while (p) { /* validate return not NULL */
strcpy (word, p); /* make copy, to preserve original */
encryptw (word); /* pass word to encryptw to encrypt word */
/* output word, original and encrypted */
printf ("word[%2zu] : %-12s : (%s)\n", ++n, p, word);
p = strtok (NULL, DELIM); /* all subsequent calls - pass NULL */
}
}
Example Use/Output
Combining your original string from your question "hello"
with the phrase from your last comment "how many words you want to encrypt"
, running the program and passing the combined string would result in:
$ ./bin/charencryptbufmulti
Enter a message to encrypt: hello how many words you want to encrypt
word[ 1] : hello : (urzyb)
word[ 2] : how : (ubk)
word[ 3] : many : (znbl)
word[ 4] : words : (jbfqf)
word[ 5] : you : (lbi)
word[ 6] : want : (jnbg)
word[ 7] : to : (gb)
word[ 8] : encrypt : (rbpelcg)
Let me know if that is what you described in your last comment and let me know if you have further questions.