Problem: take a string and move every character in the alphabet 13 times forward; for example 'hello' would be 'uryyb', but the trick here is that if there is a vowel in the element before then its 14 spaces so it would be 'urzyb'. I got the 14 space but then nothing else happens to the other letters so I keep getting 'hezlo', but if remove the // and use this line of code
message[i] = message[i] + key;`
then it doesn't do the 14 and only does 13 times. Can someone explain what I'm doing wrong and how to fix it?
#include<stdio.h>
int main()
{
char message[100], ch;
int i, key;
printf("Enter a message to encrypt: ");
gets(message);
printf("Enter key: ");
scanf("%d", &key);
for(i = 0; message[i] != '\0'; ++i){
if(message[i] >= 'a' && message[i-1] <= 'z'){
if(message[i-1] == 'e'){
message[i]=message[i] + 14;
}
//message[i] = message[i] + key;
if(message[i] > 'z'){
message[i] = message[i] - 'z' + 'a'-1 ;
}
message[i] = message[i];
}
}
printf("Encrypted message: %s", message);
return 0;
}
Output is hezlo
should be urzyb
I have three advises for you.
Don't use gets, it is deprecated and for good reason, use fgets instead.
Since you are modifying the message character by character. You cannot look back at the previous character using message[i-1] to see if that was a wovel, because it was already shifted in the previous iteration of the loop. store the previous character in a separate variable instead.
Since you are wrapping back to 'a' when you reach 'z', consider using the modulus arithmetic, which is used to cycle the numbers in a given range.
see the code below with these ideas applied to your code.
int main()
{
// ...
printf("Enter a message to encrypt: ");
fgets(message,100,stdin);
printf("Enter key: ");
scanf("%d", &key);
char p = 1; // some initial value that is not a vowel.
for(i = 0; message[i] != '\0'; ++i){
if(message[i] >= 'a' && message[i] <= 'z'){
char ch = (message[i]-'a' + key) % 26 + 'a'; // using modular math
if (strchr("aeiou",p)){
ch++; // increment by 1 if previous character was a vowel
}
p = message[i]; // save previous character
message[i]=ch; // update message.
}
}
printf("Encrypted message: %s", message);
return 0;
}
First of all, pay attention to things like this one:
if(message[i-1] == ...
because on the first iteration the index i is 0: so, i-1 is negative, and does not address a valid character of the string. This is an error. The way to solve this is that if the index (i) is 0, then there is no previous character, so you know it can not be a vowel.
Second, you state that you want to "slide" the characters: then you must include a statement to do that, like this:
message[i] = message[i] + key;
Try to describe the algorithm in plain english, then translate it in C code. Something like this:
Scan all characters, and add 13 to each; but if the preceding char is a vowel, than add 14 instead of 13.
The direct outcome of the previous sentence goes like this:
for(i = 0; message[i] != '\0'; i++) {
if (i > 0) {
// we are past the first letter
if (message[i-1]=='a' || message[i-1]=='e' || ...)
message[i] += 14;
else message[i] += 13;
} else {
// it's the first letter, there can not be a preceding vowel
message[i] += 13;
}
}
The above algorithm has some problem - mainly, it does tests for vowels on the already modified message. And it is slightly too verbose. Better to do something like this (warning, untested):
int wasvowel = 0;
char previous;
for(i = 0; message[i] != '\0'; i++) {
previous = message[i]; // save for later
if (wasvowel)
message[i] += key+1;
else message[i] += key;
wasvowel = previous=='a' || previous=='e' ...
}
The code above misses some checks; it is not clear what to do if the translated char becomes a not-letter, for example. But the general idea is there.
A note about the variable "previous" (perhaps the name is not very correct). The algorithm must consider each char in order to determine whether it is a vowel or not. But this check must be made before changing the string. Imagine what happens with words having two vowels in a row, like "aerial". The letter 'e' must be slided 14 times, ok, but the letter 'r' too. So we must remember that the letter before the 'r' was a vowel, and to do that we must preserve the 'e'. There are other methods to do that, but this one is simple.
Another note about the variable wasvowel. Prior to the cycle its value is set to 0: of course there is no vowel before the start of the message. Then in the cycle wasvowel is set, ready for the next iteration. In the last iteration, wasvowel is set again, but the value will be never used. This is, I think, acceptable in this context - it is possible that, in another context, it would not.
Because you are overwriting the main string, the e becomes r and then you can't read e to compare to anymore. That's why you are getting uryyb instead of urzyb. This is a modified code with an alternative vowel check method and another buffer for the modified string, keeping the original intact:
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char message[100], original[100], ch;
int i, key, len;
memset(message, 0, 100);
memset(original, 0, 100);
printf("Enter a message to encrypt: ");
scanf("%s", original);
printf("Enter key: ");
scanf("%d", &key);
for(i = 0, len = strlen(original); i < len; i++){
if(original[i] >= 'a' && original[i] <= 'z'){
if(i > 0 && memchr("aeiou", original[i-1], 5)){
message[i] = original[i] + 14;
}else{
message[i] = original[i] + 13;
}
}
}
for(i = 0, len = strlen(message); i < len; i++){
if(message[i] > 'z'){
message[i] = message[i] - 'z' + 'a' - 1;
}
}
printf("Encrypted message: %s", message);
return 0;
}
Edit: moved the overflow fix outside another loop.
Edit: forgot an crucial part, chars need to be unsigned.
https://onlinegdb.com/ryo2dKR5H
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.
Related
I am trying to read the command line arguments that are separated by a semicolon with a blank space front and back (such as ls ; date ; cal), but the separation part has been difficult. My codes are working when I simply put an individual command line (such as ls or date), but whenever I put the semicolon, it does not work (such as ls ; date)
Here is my C code:
void parse(char *userInput, char **splitInput)
{
//read until userInput is not end of line
while (*userInput != '\0')
{
//replace any space in userInput as '\0'
while (*userInput == ';')
{
*userInput++ = '\0';
}
//save the argument position
*splitInput++ = userInput;
//if userinput is not equal to space, read next userInput
while (*userInput != ' ' && *userInput != ';' && *userInput != '\0')
{
userInput++;
}
}
}
void execute(char **splitInput)
{
pid_t pid = fork();
if (pid > 0) //parent process
{
pid_t parent_pid;
parent_pid = wait(NULL);
}
else if (pid == 0) //child process
{
if (execvp(*splitInput, splitInput) < 0)
{
printf("%s: command not found\n", *splitInput);
exit(1);
}
}
else //error
{
perror("fort error");
}
}
void main(void)
{
char userInput[100]; //execvp's first argument
char *splitInput[100]; //execvp's second argument
while(strcmp(userInput,"quit") != 0)
{
//ask for a user input
printf("group 10> ");
//read the entire line of input
scanf("%[^\n]", userInput);
//get characters from input; stop the loop problem
getchar();
//quit if user input is equal to quit
if (strcmp(userInput, "quit") == 0)
{
exit(0);
}
//parse the input
parse(userInput, splitInput);
//execute fork
execute(splitInput);
}
}
There are a number of ways to do this. string.h provides several functions that can be used, strtok(), strsep(), strchr(), or a combination of strcspn() and strspn() depending on your needs. You can also always walk-a-pointer down the string picking out the wanted tokens from the string and ignoring whitespace and multiple-included-delimiters. There is actually good pointer learning value in approaching it this way from an education standpoint.
Any time you are looping over anything picking out various pieces, instead of trying to include multiple nested while loops, each designed to scan-forward to either skip or find a certain class of characters, it is often more advantageous to use a state-loop where you use one of more flags to keep track of differing states. (line in-word reading character, or between words reading delimiters or whitespace, etc..). That way no nested loops are required and you simply use a single loop to loop over each character responding accordingly depending on your current state.
Putting that in work to scan down your string and pick out each of the words terminated by a delimiter ';' or by whitespace, and keeping a single state flag int in; to track whether you are in-word reading characters (in = 1;) or between words handling spaces and delimiters (in = 0;) and use char *sp as the start-pointer pointing to the beginning of each word and userInput as the end-pointer pointing to the current character being read, you could do:
void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
int in = 0; /* simple in-word flag 0-false/1-true */
size_t n = 0; /* counter to protect splitInput bounds */
char *sp = userInput; /* start-pointer initialized to userInput */
while (n < nptrs - 1) { /* loop while pointers remain unfilled */
/* if at end, is whitespace or a delimiter */
if (!*userInput || isspace(*userInput) || *userInput == delim) {
if (in) { /* if in word */
splitInput[n++] = sp; /* set pointer to start-pointer */
splitInput[n] = NULL; /* set next pointer NULL */
}
in = 0; /* reset in flag zero */
if (*userInput) /* if space or delim, nul-terminate */
*userInput = 0;
else /* otherwise */
return; /* at end-of-string */
}
else { /* normal char */
if (!in) { /* if not in-word */
sp = userInput; /* set start-pointer to 1st good char */
in = 1; /* set in-word flag true */
}
}
userInput++; /* advance to next char */
}
}
(note: above the delim character is passed as a parameter along with nptrs to pass the number of pointers you have available so you can protect your pointer array bounds while filling pointers. Also note, the function always sets the next pointer in your array to NULL as a sentinel allowing you to loop over the pointers in your array in main() until NULL is reached since you don't return the number of pointers used, either as the function return or through a pointer parameter)
A simple example that parses the words from " my; ; ; dog ;;; has;fleas ;" using ';' or whitespace as delimiters could be:
#include <stdio.h>
#include <ctype.h>
#define NPTR 32 /* if you need a constant, #define one (or more) */
void parse(char *userInput, char **splitInput, char delim, size_t nptrs)
{
int in = 0; /* simple in-word flag 0-false/1-true */
size_t n = 0; /* counter to protect splitInput bounds */
char *sp = userInput; /* start-pointer initialized to userInput */
while (n < nptrs - 1) { /* loop while pointers remain unfilled */
/* if at end, is whitespace or a delimiter */
if (!*userInput || isspace(*userInput) || *userInput == delim) {
if (in) { /* if in word */
splitInput[n++] = sp; /* set pointer to start-pointer */
splitInput[n] = NULL; /* set next pointer NULL */
}
in = 0; /* reset in flag zero */
if (*userInput) /* if space or delim, nul-terminate */
*userInput = 0;
else /* otherwise */
return; /* at end-of-string */
}
else { /* normal char */
if (!in) { /* if not in-word */
sp = userInput; /* set start-pointer to 1st good char */
in = 1; /* set in-word flag true */
}
}
userInput++; /* advance to next char */
}
}
int main (void) {
char s[] = " my; ; ; dog ;;; has;fleas ;", *split[NPTR] = { NULL }, **p = split;
parse (s, split, ';', NPTR);
while (*p)
printf ("'%s'\n", *p++);
}
(note: the header ctype.h is included to make use of the isspace() function to test for whitespace rather than stringing together if() statements checking for space, '\t' or '\n'directly. This is generally good practice.)
Example Use/Output
$ ./bin/split_ptr_arr3
'my'
'dog'
'has'
'fleas'
Note in the output above all included whitespace is removed.
Look things over and let me know if you have questions. There are literally dozens of way to approach splitting strings, this is just one common and basic approach.
I wrote a code that takes a sentence and outputs every each word in a line. But I also want to write the size of each word next to it.
Input:
Hi my name is
Current output:
Hi
my
name
is
Desired output:
Hi(2)
my(2)
name(4)
is(2)
My current Code:
#include <stdio.h>
#define MAX 100
int main(void) {
int c = 0;
size_t n = 0;
printf("\n Enter a sentence.\n\n input: ");
/* read up to 100 characters from stdin, print each word on a line */
while (n < MAX && (c = getchar()) != EOF && c != '\n')
{
if (c == ' ')
printf("\n");
else
printf("%c", c);
n++;
}
printf("\n");
if (n == MAX) /* read and discard remaining chars in stdin */
while ((c = getchar()) != '\n' && c != EOF);
return 0;
}
How can I do that?
For completeness a different approach reading the whole input in one call and then tokenising it:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX (100)
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
char s[MAX +1];
printf("\n Enter a sentence.\n\n input: ");
/* read up to 100 characters from stdin, print each word on a line */
if (NULL == fgets(s, sizeof s, stdin))
{
if (ferror(stdin))
{
perror("fgets() failed");
result = EXIT_FAILURE;
}
}
else
{
s[strcspn(s, "\r\n")] = '\0'; /* chop off carriage return, line feed, if any */
for (char * pc = strtok(s, " "); NULL != pc; pc = strtok(NULL, " "))
{
printf("%s (%zu)\n", pc, strlen(pc));
}
}
return result;
}
As the read buffer is never explicitly used the following variation is possible as well:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX (100)
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
printf("\n Enter a sentence.\n\n input: ");
{
/* read up to 100 characters from stdin, print each word on a line */
char * pc = fgets((char[MAX+1]), MAX+1, stdin);
if (NULL == pc)
{
if (ferror(stdin))
{
perror("fgets() failed");
result = EXIT_FAILURE;
}
}
else
{
pc[strcspn(pc, "\r\n")] = '\0'; /* chop off carriage return, line feed, if any */
for (pc = strtok(pc, " "); NULL != pc; pc = strtok(NULL, " "))
{
printf("%s (%zu)\n", pc, strlen(pc));
}
}
}
return result;
}
Have one more variable and print out when you hit space.
size_t len = 0;
/* read up to 100 characters from stdin, print each word on a line */
while (n < MAX && (c = getchar()) != EOF && c != '\n')
{
if (c == ' ') {
printf("(%u)\n", len);
len = 0;
}
else {
len++;
printf("%c", c);
}
n++;
}
In addition to the good answer by #kiranBiradar, you may want to add an additional variable that allows you to track whether you are in-a-word reading characters or outside-a-word reading whitespace. (using a simple int value as a flag set to 1 (true) for in-word or 0 (false) not-in-word is all you need) This will allow you to ignore leading whitespace, multiple included whitespace, or trailing whitespace in your input, e.g., if your input was similar to:
" my dog has fleas and my cat has none "
Unless you are keeping track of the state of your read, whether you are in-word / not-in-word, you will be outputting multiple occurrences of "(0)\n" each time a whitespace character is read. By keeping a flag of whether you are in / not-in a word and setting it zero when you encounter your first whitespace after being in-word reading non-whitespace characters allows you to only output the length once on the first whitespace encountered.
Additionally, conditioning your read on c != '\n' will skip outputting the length of the final word, unless you include additional code after you exit your read loop.
Also by including <ctype.h> you have the isspace() macro available to check for all whitespace (e.g, space, tab, newline, backspace, vertical-tab, etc...) It greatly simplifies your conditional checks.
Putting it altogether you could do:
#include <stdio.h>
#include <ctype.h> /* for isspace() */
int main (void) {
int c = 0, in = 0, len = 0; /* char, in/out flag, length */
fputs ("enter text: ", stdout); /* prompt for text */
fflush (stdout); /* (optional), but recommended */
while ((c = getchar()) != EOF) { /* loop reading chars until EOF */
if (isspace (c)) { /* if input is space */
if (in) { /* check if in-word */
printf ("(%d)\n", len); /* output (len) */
len = 0; /* reset len zero */
in = 0; /* set in flag zero (false) */
}
if (c == '\n') /* if space is \n */
break; /* break read loop */
}
else { /* if not whitespace */
putchar (c); /* output char */
len++; /* increment length */
in = 1; /* set in flag 1 (true) */
}
}
}
(note: there is no reason to limit your read to n < MAX unless you simply want to arbitrarily limit your read of characters to the first 100-characters. There is no array being filled or other storage being occupied by the character c (aside from it's one byte). You could read billions if your input contained them)
Example Use/Output
$ ./bin/getchar_word_len
enter text: my dog has fleas and my cat has none
my(2)
dog(3)
has(3)
fleas(5)
and(3)
my(2)
cat(3)
has(3)
none(4)
Look over both answers and let either of us know if you have further questions. If you are confused by the logic, take out an 8.5x11 sheet of paper and work through the logic of the loop for each character starting at the beginning of your input. It will make sense by the time you work through the first word.
Program compiles, but when I try to use it, I get an endless loop. Where did i do wrong. I put comments into what I am trying to accomplish.
I have tried changing it to a for loop, but I still get issues. I think I am suppose to stick with a while loop to accomplish what I am trying to accomplish.
//Declare the arrays to hold the strings
char str[21], vowels[21], consonants[21];
int i=0;
//Declare the pointers
char *strPointer, *vowelPointer, *consonantPointer;
//Print out the prompt to the user
printf("Enter a string (20 characters maximum): ");
//Scan the user input into str
//Only allow 20 characters
scanf("%s", str);
//Set strPointer to the beginning of the user's string
strPointer = str;
//Set vowelPointer to the beginning of the vowels string
vowelPointer = vowels;
//Set consonantPointer to the beginning of tht consonant string
consonantPointer = consonants;
//Loop through the user's string until the end of the string
while(*strPointer !='\0')
{
//Check if what strPointer is pointing to is a vowel
if(strPointer[i]=='A'||strPointer[i]=='a'||strPointer[i]=='E'||strPointer[i]=='e'||strPointer[i]=='I'||strPointer[i]=='i'||strPointer[i]=='O'||strPointer[i]=='o'||strPointer[i]=='U'||strPointer[i]=='u')
{
//Move the letter from strPointer to vowelPointer
strPointer=vowelPointer
;
//Move the vowelPointer
vowelPointer=vowels
;
}
else
{
//Move the letter from strPointer to consonantPointer
strPointer=consonantPointer
;
//Move the consonantPointer
consonantPointer=consonants
;
}
//Move the strPointer
strPointer=str;
}
//Add null terminators where appropriate
strPointer[21]='\0';
str[21]='\0';
//Set the vowel and consonant pointers back to the beginning of their strings
vowelPointer[0];
consonantPointer[0];
//Print the original string and the resulting vowel and consonant strings
printf("Original string: %s\n", str);
printf("%s\n", vowelPointer);
printf("%s\n", consonantPointer);
The output is explained in my printf statements at the end. Input string, reprinted with the vowels and consonants separated and listed.
I would like to make some suggestions to help you out.
First, when you are scanning from user input, it is wise to use a buffer. Then, strcpy the contents of the buffer into the char array. This will help to prevent overflow. Please see the below code for more details about this topic.
Second, you can use a for loop to iterate through each of the letters. I hope my answer will help you understand what I mean.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
int main(void)
{
//Declare the arrays to hold the strings
char c, str[21], vowels[21], consonants[21], buffer[21];
int i = 0, j = 0, h = 0;
//Declare the pointers
char *strPointer, *vowelPointer, *consonantPointer;
//Print out the prompt to the user
printf("Enter a string (20 characters maximum): ");
//Scan the user input into str
scanf("%s", buffer);
// Copy the buffer into the str
strcpy(str, buffer);
// go letter by letter checking if it is a vowel or consonant
for (int i = 0; str[i]; i++)
{
// make the character uppercase
c = toupper(str[i]);
// if the letter is a vowel add the letter to the vowel array,
// then increase the position
if (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U')
vowels[j++] = str[i];
else
// add the letter to the consonants array, then increase the position
consonants[h++] = str[i];
}
// properly terminate the strings
vowels[j] = '\0';
consonants[h] = '\0';
//Print the original string and the resulting vowel and consonant strings
printf("Original string: %s\n", str);
printf("%s\n", vowels);
printf("%s\n", consonants);
return 0;
}
You invoke an infinite loop by setting:
strPointer=str;
at the end of your while (*strPointer !='\0') loop. This resets the address held by strPointer to the beginning of str. strPointer is never incremented, so your loop loops with strPointer pointing to the first character in str over-and-over-and-over again...
Further, you initialize vowelPointer=vowels; which sets the address of vowelPointer to point to the beginning element of the uninitialized vowels array. The same occurs with consonantPointer=consonants;
Since both vowelPointer and consonantPointer point to arrays declared with automatic storage duration, you invoke Undefined Behavior when you attempt to access the uninitialized values with:
printf("%s\n", vowelPointer);
printf("%s\n", consonantPointer);
(but luckily you never get there because of your endlesss loop over while (*strPointer !='\0'))
Additionally, validate EVERY input and affirmatively protect your array bounds by using the field-width modifier with scanf (or better, use fgets()). For example:
//Only allow 20 characters
if (scanf( "%20s", str) != 1) {
fputs ("error: (user canceled input)\n", stderr);
return 1;
}
(note: when reading with "%s" the trailing '\n' is not consumed and will remain in your input buffer)
To fix your pointer problems, you can do something similar to the following:
//Declare the arrays to hold the strings
char str[21], vowels[21], consonants[21];
size_t vidx = 0, cidx = 0; /* indexes for vowels/consonants */
...
//Loop through the user's string until the end of the string
while (*strPointer)
{
//Check if what strPointer is pointing to is a vowel
if (strPointer[i]=='A'|| strPointer[i]=='a'||
strPointer[i]=='E'|| strPointer[i]=='e'||
strPointer[i]=='I'|| strPointer[i]=='i'||
strPointer[i]=='O'|| strPointer[i]=='o'||
strPointer[i]=='U'|| strPointer[i]=='u')
{
//Copy the letter from strPointer to vowelPointer
if (vidx < 20) {
vowelPointer[vidx] = *strPointer;
vidx++;
}
/* or using pointer arithmetic */
// if (vowelPointer - vowels < 20) {
// *vowelPointer = *strPointer;
// vowelPointer++;
// }
}
else {
//Copy the letter from strPointer to consonantPointer
if (cidx < 20) {
consonantPointer[cidx] = *strPointer;
cidx++;
}
/* same alternative available for consonantPointer */
}
//Move the strPointer
strPointer++;
}
//Add null terminators where appropriate
vowelPointer[vidx] = 0;
consonantPointer[cidx] = 0;
//Reset the ponters
vowelPointer = vowels;
consonantPointer = consonants;
//Print the original string and the resulting vowel and consonant strings
printf("Original string: %s\n", str);
printf("%s\n", vowelPointer);
printf("%s\n", consonantPointer);
If you are still stuck, you can put it altogether in a short, slightly more concise example as:
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* don't skimp on buffer size */
int main (void) {
char str[MAXC], cons[MAXC], vowels[MAXC], /* arrays */
*strptr = str, *consptr = cons, *vowelptr = vowels; /* pointers */
fputs ("Enter a string (1022 characters maximum): ", stdout);
if (!fgets (str, MAXC, stdin)) { /* validate EVERY read */
fputs ("error: (user canceled input)\n", stderr);
return 1;
}
while (*strptr) { /* loop over each character */
char lc = tolower (*strptr); /* convert to lowercase to compare */
if (lc == 'a' || lc == 'e' || lc == 'i' || lc == 'o' || lc == 'u')
*vowelptr++ = *strptr; /* copy vowel to array */
else if (!isspace (*strptr)) /* otherwise if not whitespace */
*consptr++ = *strptr; /* copy to consonant array */
strptr++; /* advance string pointer */
}
*vowelptr = *consptr = 0; /* nul-terminate arrays */
printf ("str : %scons : %s\nvowels: %s\n", str, cons, vowels);
}
(question: do you know why there is no '\n' needed after "str : %s" in the printf above?)
Note above that the character is converted to lowercase before comparison for a vowel to cut in-half the number of conditionals needed to check for a vowel. Don't skimp on buffer size. Generally you have 1M of stack space (4M on Linux). Use a buffer of at least 256-chars, or a simple 1K buffer as used above. Also note if you wanted to print using the pointer, you would simply reset them to point to the original arrays immediately after exiting the loop, e.g.
strptr = str;
consptr = cons;
vowelptr = vowels;
Regardless, you can print using the arrays or the pointers after resetting them as both will both point to the same address. See C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
Example Use/Output
$ ./bin/sepvowels
Enter a string (1022 char max): A quick brown fox jumps over the lazy dog
str : A quick brown fox jumps over the lazy dog
cons : qckbrwnfxjmpsvrthlzydg
vowels: Auioouoeeao
I am trying to delete the blank space between a string and print out the first word with isalpha() function.
When I print out, only the first letter prints out. exempel "hello big panda" I get "hhhhh" but I want the hole word "hello" instead
int main()
{
char inputString[]={"hello big panda"};
int k=0;
int i=0;
do
{
inputString[i];
i++;
}
while (inputString[i]=isalpha(inputString[i]));
for(i=0; inputString[i] !='\0' ;i++)
{
for (k=i; inputString[k] != '\0'; k++)
{
inputString[k] =inputString[i];
}
}
printf("%s", inputString);
return 0;
}
done this:
int printfirstword(char sentence[])
{
int k=0;
int i=0;
while (isalpha(sentence[i])) //checking for the first blank space
{
i++;
}
sentence[i] = '\0';
printf("%s\n", sentence);
return 0;
}
int main()
{
char sentence[100];
int wordNumber;
char answer;
printfirstword("Hello there")
return0;
}
But I don't want to change the string that is passed to it
What you can simply do is use a while loop instead of your do-while. You can simply increment i until you find the index of first blank space. Then using the value of i you can insert '\0' in your string. Output it and you are done. :)
#include <stdio.h>
#include<ctype.h>
int main()
{
char inputString[]={"hello big panda"};
int k=0;
int i=0;
while (isalpha(inputString[i])) //checking for the first blank space
{
i++;
}
inputString[i] = '\0';
printf("%s", inputString);
return 0;
}
If you would like to keep the original string then you could simply make a new string say newStr and then
while (isalpha(inputString[i])) //checking for the first blank space
{ newStr[i]=inputString[i]; //copy first word into newStr
i++;
}
newStr[i] = '\0';
printf("%s", newStr);
Your function must do 3 things as it works through the sentence finding words. (1) always check for the end of the string to prevent an attempted read beyond the end of the sentence; and (2) locate and print the requested word at the index given; and (3) handle the condition where the user requests a word index greater than that available.
(you should always test the sentence you are passed in the function to make sure the pointer isn't a NULL pointer, and that the contents of the sentence is simply the '\0' character indicating an empty-string)
An easy way to do this (after you have tested the input string), is to set up a continual loop, that repeatedly read the characters of a word, checks if it is the word to print (if so it prints), and if not read and discard all the non-alpha characters before the next word, and then repeats.
Something simple like the following works. It takes the sentence (or updated position within the sentence) and the index for the word to print zero-indexed, e.g. (0, 1, 2, ...) and then loops a described above.
(note: you can change the zero-index scheme to a 1, 2, 3, ... word-number scheme by initializing n=1; instead of 0 -- but since everything in C is zero indexed, that is left to you)
#include <stdio.h>
#include <ctype.h>
int prnword (const char *s, int nwrd)
{
int n = 0; /* word counter */
char *p = s; /* pointer to s */
if (!s || !*s) { /* test s not NULL and not empty */
fprintf (stderr, "error: string NULL, empty or at end.\n");
return 0;
}
for (;;) { /* loop continually until exit condition reached */
while (*p && isalpha(*p)) { /* loop over chars in s */
if (n == nwrd) /* if requested index */
putchar (*p); /* print all chars */
p++; /* increment pointer */
}
while (*p && !isalpha(*p)) /* iterate find next alpha */
p++;
if (++n > nwrd) /* if past our word, break */
break;
if (!*p) /* if end reached, break */
break;
}
if (n <= nwrd) { /* check request exceeds avaialble words */
fprintf (stderr, "error: request word '%d' "
"exceeds available wprds indexes.\n", nwrd);
return 0;
}
putchar ('\n'); /* tidy up with new line */
return p - s; /* return number of chars to next alpha */
}
int main (void) {
char str[] = "hello big panda";
int nchars = 0;
/* example -- all words in order
* passing update string position
*/
nchars = prnword (str, 0);
nchars += prnword (str + nchars, 0);
nchars += prnword (str + nchars, 0);
putchar ('\n');
/* request exceed available zero-based word indexes */
nchars = 0;
nchars += prnword (str, 3);
putchar ('\n');
/* print 2nd word only */
nchars = 0;
nchars = prnword (str, 1);
putchar ('\n');
return 0;
}
Example Use/Output
Note the first block of calls to prnword print each of the words in the sentence, saving the number of characters returned by prior calls and using that to start the function reading the 1st character of the desired word, meaning you are always looking for word index 0.
The second call intentionally gives an index one past the last word to force handling the error.
And finally, the last call simply says "Go print word 2" (index 1) starting from scratch.
Example Use/Output
$ ./bin/words
hello
big
panda
error: request word '3' exceeds available wprds indexes.
big
Look things over and let me know if you have questions.
I am trying to write a program that reads the stdin stream looking for words (consecutive alphabetic characters) and for each word rotates it left to the first vowel (e.g. "friend" rotates to "iendfr") and writes this sequence out in place of the original word. All other characters are written to stdout unchanged.
So far, I have managed to reverse the letters, but have been unable to do much more. Any suggestions?
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#define MAX_STK_SIZE 256
char stk[MAX_STK_SIZE];
int tos = 0; // next available place to put char
void push(int c) {
if (tos >= MAX_STK_SIZE) return;
stk[tos++] = c;
}
void putStk() {
while (tos >= 0) {
putchar(stk[--tos]);
}
}
int main (int charc, char * argv[]) {
int c;
do {
c = getchar();
if (isalpha(c) && (c == 'a' || c == 'A' || c == 'e' || c == 'E' || c == 'i' || c == 'o' || c == 'O' || c == 'u' || c == 'U')) {
push(c);
} else if (isalpha(c)) {
push(c);
} else {
putStk();
putchar(c);
}
} while (c != EOF);
}
-Soul
I am not going to write the whole program for you, but this example shows how to rotate a word from the first vowel (if any). The function strcspn returns the index of the first character matching any in the set passed, or the length of the string if no matches are found.
#include <stdio.h>
#include <string.h>
void vowelword(const char *word)
{
size_t len = strlen(word);
size_t index = strcspn(word, "aeiou");
size_t i;
for(i = 0; i < len; i++) {
printf("%c", word[(index + i) % len]);
}
printf("\n");
}
int main(void)
{
vowelword("friend");
vowelword("vwxyz");
vowelword("aeiou");
return 0;
}
Program output:
iendfr
vwxyz
aeiou
There are a number of ways your can approach the problem. You can use a stack, but that just adds handling the additional stack operations. You can use a mathematical reindexing, or you can use a copy and fill solution where you copy from the first vowel to a new string and then simply add the initial characters to the end of the string.
While you can read/write a character at a time, you are probably better served by creating the rotated string in a buffer to allow use of the string within your code. Regardless which method you use, you need to validate all string operations to prevent reading/writing beyond the end of your input and/or rotated strings. An example of a copy/fill approach to rotating to the first vowel in your input could be something like the following:
/* rotate 's' from first vowel with results to 'rs'.
* if 's' contains a vowel, 'rs' contains the rotated string,
* otherwise, 'rs' contais 's'. a pointer to 'rs' is returned
* on success, NULL otherwise and 'rs' is an empty-string.
*/
char *rot2vowel (char *rs, const char *s, size_t max)
{
if (!rs || !s || !max) /* validate params */
return NULL;
char *p = strpbrk (s, "aeiou");
size_t i, idx, len = strlen (s);
if (len > max - 1) { /* validate length */
fprintf (stderr, "error: insuffieient storage (len > max - 1).\n");
return NULL;
}
if (!p) { /* if no vowel, copy s to rs, return rs */
strcpy (rs, s);
return rs;
}
idx = p - s; /* set index offset */
strcpy (rs, p); /* copy from 1st vowel */
for (i = 0; i < idx; i++) /* rotate beginning to end */
rs[i+len-idx] = s[i];
rs[len] = 0; /* nul-terminate */
return rs;
}
Above, strpbrk is used to return a pointer to the first occurrence of a vowel in string 's'. The function takes as parameters a pointer to a adequately sized string to hold the rotated string 'rs', the input string 's' and the allocated size of 'rs' in 'max'. The parameters are validated and s is checked for a vowel with strpbrk which returns a pointer to the first vowel in s (if it exists), NULL otherwise. The length is checked against max to insure adequate storage.
If no vowels are present, s is copied to rs and a pointer to rs returned, otherwise the pointer difference is used to set the offset index to the first vowel, the segment of the string from the first vowel-to-end is copied to rs and then the preceding characters are copied to the end of rs with the loop. rs is nul-terminated and a pointer is returned.
While I rarely recommend the use of scanf for input, (a fgets followed by sscanf or strtok is preferable), for purposes of a short example, it can be used to read individual strings from stdin. Note: responding to upper/lower case vowels is left to you. A short example setting the max word size to 32-chars (31-chars + the nul-terminating char) will work for all known words in the unabridged dictionary (longest word is 28-chars):
#include <stdio.h>
#include <string.h>
enum { BUFSZ = 32 };
char *rot2vowel (char *rs, const char *s, size_t max);
int main (void)
{
char str[BUFSZ] = {0};
char rstr[BUFSZ] = {0};
while (scanf ("%s", str) == 1)
printf (" %-8s => %s\n", str, rot2vowel (rstr, str, sizeof rstr));
return 0;
}
Example Use/Output
(shamelessly borrowing the example strings from WeatherVane :)
$ echo "friend vwxyz aeiou" | ./bin/str_rot2vowel
friend => iendfr
vwxyz => vwxyz
aeiou => aeiou
Look it over and let me know if you have any questions. Note: you can call the rot2vowel function prior to the printf statement and print the results with rstr, but since the function returns a pointer to the string, it can be used directly in the printf statement. How you use it is up to you.