So I am trying to write a program that takes a sentence and prints it out from the third word. Ex one two three four should print out three four.
Now this code works but I have no idea why as the logic under the else statement make it seem like it should not.
Would be thankful if someone could explain why it works like this.
Here is the code:
#include <stdio.h>
#include <string.h>
#define SIZE 100
int main(void) {
char arr[SIZE];
char *point;
char again = 'n';
do {
int count = 0;
for (int i = 0; i < SIZE; i++) {
arr[i] = '\0';
}
printf("Enter a sentence:");
gets(arr);
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
if (count < 2) {
printf("The sentence is to short!\n");
} else {
count = 1; //shouldn't this be count = 0?
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
point = &arr[i + 2]; //shouldn't this be [i+1]?
}
}
printf("%s\n", point);
}
printf("Do you want to try again? (y/n)");
scanf("%c", &again);
while (getchar() != '\n');
} while (again == 'y' || again == 'Y');
return 0;
}
Your code has multiple problems:
You should never use gets(). This function has been removed from the C Standard because it cannot be given the maximum number of characters to write to the destination buffer, so any sufficiently long line from the input stream will cause undefined behavior. This is a classic security flaw. Use fgets() instead.
The loop while (getchar() != '\n'); will cause an infinite loop if there is no newline before the end of file, which will happen if you redirect input an empty file. You should also check for EOF:
while ((c = getchar()) != EOF && c != '\n')
continue;
There is no need to initialize the destination array, but you should check if the input operation succeeded, by comparing the return value of fgets() to NULL.
When iterating through the array to count spaces, you should stop at the null terminator. The contents of the array beyond the null terminator is indeterminate after the input operation, even if you initialized it prior to the call.
The code to skip the words is cumbersome and not easy to validate. Indeed point = &arr[i+2]; should be point = &arr[i+1].
words might be separated by more than one space, and initial spaces should be ignored.
Here is a corrected version using string functions strspn and strcspn to skip blanks and non-blanks:
#include <stdio.h>
#include <string.h>
#define SIZE 100
#define WS " \t\n\r\v\f" /* white space characters */
int main(void) {
char arr[SIZE];
char *p;
for (;;) {
printf("Enter a sentence:");
if (fgets(arr, sizeof arr, stdin) == NULL)
break;
p = arr;
p += strspn(p, WS); /* skip initial spaces */
p += strcspn(p, WS); /* skip first word */
p += strspn(p, WS); /* skip spaces */
p += strcspn(p, WS); /* skip second word */
p += strspn(p, WS); /* skip spaces */
if (*p == '\0') {
printf("The sentence is too short!\n");
} else {
printf("%s", p);
}
printf("Do you want to try again? (y/n)");
if (fgets(arr, sizeof arr, stdin) == NULL)
break;
if (*arr != 'y' && *arr != 'Y')
break;
}
return 0;
}
Another simple way to handle the word count is to walk-a-pointer down your string in a state loop keeping track of whether you are in a word (if so increase word count), otherwise you are not in a word and just keep walking down the buffer (i.e. iterating over each char) until you find the next word (or end of string).
The logic is simple, after filling your buffer, and setting a pointer to it, e.g.
#define MAXC 1024 /* buffer size (don't skimp) */
#define NWORD 3 /* output beginning with NWORD word */
...
char buf[MAXC] = "", /* buffer to hold line */
*p = buf; /* pointer to walk down buffer */
int n = 0, /* word counter */
in = 0; /* flag - in a word */
Just loop checking each character with isspace() and handle setting your in flag to either 1 (in word) or 0 (in space before or between words) incrementing your counter each time you go in a new word, and exiting the loop when your count reaches 3, e.g.
for (; *p; p++) { /* loop over each char */
if (!in && !isspace(*p)) { /* if not in word and not space */
in = 1, n++; /* set in flag, increment words */
if (n == NWORD) /* if 3rd word, break */
break;
}
else if (isspace(*p)) /* if space */
in = 0; /* unset in flag */
}
Putting it altogether in a short example, you could do something similar to the following which takes input until the Enter key is pressed alone on an empty line, and outputting each sentence entered beginning with the third-word, or displaying the error "too few words." if a sentence with less that three words is entered, e.g.
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* buffer size (don't skimp) */
#define NWORD 3 /* output beginning with NWORD word */
int main (void) {
for (;;) { /* loop continually until empy-line */
char buf[MAXC] = "", /* buffer to hold line */
*p = buf; /* pointer to walk down buffer */
int n = 0, /* word counter */
in = 0; /* flag - in a word */
fputs ("\nenter sentence: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin) || *buf == '\n') { /* read line */
puts ("all done!");
break;
}
for (; *p; p++) { /* loop over each char */
if (!in && !isspace(*p)) { /* if not in word and not space */
in = 1, n++; /* set in flag, increment words */
if (n == NWORD) /* if 3rd word, break */
break;
}
else if (isspace(*p)) /* if space */
in = 0; /* unset in flag */
}
if (n == NWORD) /* if 3 or more words */
fputs (p, stdout);
else /* other wise handle error */
fputs ("too few words.\n", stderr);
}
return 0;
}
Example Use/Output
$ ./bin/thirdword
enter sentence: one two three four five
three four five
enter sentence: one two
too few words.
enter sentence: one two three
three
enter sentence:
all done!
Look things over and let me know if you have further questions.
count = 1; //shouldn't this be count = 0? and point = &arr[i + 2]; //shouldn't this be [i+1]?
The following answers both questions.
count count count count
0 1 2 3
one two three four
i+0 i+1 i+2 i+3
point = &arr[i + 2]; along with printf("%s\n", point); says that print all characters from address of arr[i + 2] till seeing \0 character
Related
#include <stdlib.h>
int main(){
int sum=1;
char sent[50];
fgets(sent, sizeof(sent), stdin);
for(int i=0;i<=strlen(sent);i++){
if(i==' '){
sum++;
}
}
scanf("%s", sum);
return 0;
}
What I want is to find how many words there are in the sentence. How can I find out how long the sentence is? Thanks in advance.
Continuing from the pseudo-code from #chux, an implementation would be:
#include <stdio.h>
#include <ctype.h>
#define MAXS 50 /* if you need a constant, #define one (or more) */
int main (void){
int sum = 0, i = 0, inword = 0; /* use inword as a flag, in/out word */
char sent[MAXS];
if (fgets(sent, sizeof(sent), stdin) == NULL) { /* read/VALIDATE input */
fputs ("(user canceled input)\n", stderr);
return 1;
}
while (sent[i]) { /* loop over each character */
if (isspace(sent[i])) { /* is the character a space? */
inword = 0; /* set flag to false */
}
else { /* otherwise */
if (!inword) /* if we were reading spaces, now in word */
sum += 1; /* increment sum */
inword = 1; /* set flag true */
}
i++; /* increment loop counter */
}
printf ("whitespace separated words: %d\n", sum);
}
You keep a "state" variable inword that tracks the state of whether you are "in a word" reading characters, or before, between or after a word reading whitespace. This also ensures that leading whitespace, multiple-included whitespace and trailing whtiespace is ignored.
What I want is to find how many words there are in the sentence.
Set a flag indicating if parsing is in a word or not and count transitions from out to in
// Pseudo code
count = 0
in_word = 0
for each character `i` in `sent`
if (sent[i] is a letter)
if (!in_word) count++
in_word = true
else
in_word = false
print count
How can I find out how long the sentence is?
Usually a simple strlen(sent) is sufficient.
More advanced ideas include discounting leading/trailing white-space.
Sample algorithm
// Pseudo code
for each character `i` in `sent`
if character is not a space, break
offset = i
position_of_last_non_space = 0
for remaining characters `i` in `sent`
if character is not a space
position_of_last_non_space = i
sentence_length = position_of_last_non_space - offset + 1
I have got a different approach to the problem here :
the idea is that 2 words in a sentence are separated by one or more spaces in between.
here is the altered code with my approach :-
#include <stdio.h>
#include <string.h>
int main()
{
char sent[200];
int sum = 1, i;
fgets(sent, sizeof(sent), stdin);
for (i = 0; sent[i] != '\0'; i++)
{
if (sent[i] == ' ' && sent[i+1] != ' ')
sum++;
}
printf("%d\n", sum);
return 0;
}
The output of my code is incorrect. For example, if I input "joy is joyful", then want to remove the word "joy", the output should be " is joyful", but instead the output is the same as the input.
Here is the full code:
#include<stdio.h>
#include<conio.h>
#include<string.h>
void print(char string[100]);
main()
{
char string[100], remove[100];
int stringLen, removeLen, i, j, k, l, count, location, sLen, ij, counter = 0;
printf("Enter any string: ");
gets(string);
printf("Enter word to remove: ");
gets(remove);
printf("\nString before removing '%s': ", remove);
print(string);
stringLen = strlen(string);
sLen = stringLen;
removeLen = strlen(remove);
for(i=0; i<stringLen; i++)
{
count = 0;
for(j=0; j<removeLen; j++)
{
if(string[i+j] == remove[j])
{
count++;
location = i;
ij = i+j;
}
}
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
if(counter == removeLen)
{
for(l=0; l<count; l++)
{
for(k=location; k<sLen; k++)
{
string[k] = string[k+1];
}
sLen--;
}
}
}
printf("\n\nString after removing '%s':", remove);
print(string);
getch();
return 0;
}
void print(char string[100])
{
printf("\n%s", string);
}
I tried making this part:
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
To this and it worked:
if(count == removeLen)
{
if(string[ij+1] != '\0' && string[ij+1] != ' ' && string[ij+1] != '\n')
{
counter = count - 1;
}
else
{
counter = count;
}
}
What seems to be the problem with the original one?
For pf all, never, ever, EVER, use gets(). It is so prone to exploit by buffer overrun that is have been removed from the C-library beginning with C11. For more discussion see: Why gets() is so dangerous it should never be used!
In your word replacement, you are not worrying about removing leading or trailing whitespace before or after the word you remove and you only remove that word if it is not a substring in a larger word or a word followed by punctuation. (this is fine -- but in isolating an removing words you will generally want to takes what is left into consideration)
You can simplify what you are attempting to do and reduce the complete algorithm to a single loop over the character in the string. You simply keep three indexes (or counters if you want to think of it that way). You need a read-index, the next character to be read. You need a write-index, the next location in the string to be written. And finally you need a remove-index to the characters in the substring to be removed.
Here you simply loop over the characters in the string with your read-index. Your read and write indexes begin the same. If a letter matches the first letter in your remove substring, you increment your remove-index and loop again. If a sequence of characters match all characters in your remove substring, on the next iteration your substring index will be at its nul-terminating character.
Now you can test if the next character under the read-index in your string is a space (using the isspace() macro) or testing if you are at the end of your original string. If either case is true, you simply subtract the substring length from your write-index and continue on -- effectively removing the substring from your original string. There are no multiple-loops needed, you are essentially working through each character of the original keep track of where you are (the state) with the substring index.
A short example approaching it this way could be something like the following. The function remove_substr(), reads the characters in str and removes each isolated occurrence of substr within it updating the original str in-place:
int remove_substr (char *str, const char *substr)
{
if (!strstr (str, substr)) /* if substr not found in str */
return 0; /* return 0 - nothing replaced */
size_t sslen = strlen (substr), /* length of substr */
i = 0, j = 0, n = 0; /* read, write, substr indexes */
do { /* loop over str (including '\0') */
if (!substr[n]) { /* substr found (at substr '\0') */
/* if at end of str or whitespace */
if (!str[i] || isspace((unsigned char)str[i]))
j -= sslen; /* subtract sslen from write index */
n = 0; /* reset substr index */
}
str[j++] = str[i]; /* copy from read to write index */
if (str[i] == substr[n]) /* if char matches substr */
n++; /* increment substr counter */
} while (str[i++]); /* exit after '\0' processed */
return 1; /* return replacements made */
}
A simple type int was chosen for the return type to indicate 0 no removals took place, or 1 indicating that occurrences of substr were removed from str.
A short example calling the function could be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 1024
/* insert function here */
int main (void) {
char str[MAXC] = "", /* storage for string */
substr[MAXC] = ""; /* storage for substring */
fputs ("enter string: ", stdout); /* prompt for string */
if (!fgets (str, sizeof str, stdin)) /* read/validate input */
return 1;
str[strcspn(str, "\n")] = 0; /* overwrite '\n' with '\0' */
fputs ("enter substr: ", stdout); /* ditto for substr */
if (!fgets (substr, sizeof substr, stdin))
return 1;
substr[strcspn(substr, "\n")] = 0;
if (remove_substr (str, substr)) /* remove all substr in str */
printf ("\nresult: '%s'\n", str); /* output updated str if removals */
else
puts ("\nno replacements made"); /* otherwise output no replacements */
}
Simply run the program and you will be prompted to input the string and the substring to remove. Currently each of the strings used are limited to MAXC (1024 characters), adjust to meet your needs -- but don't skimp on buffer size.
Example Use/Output
$ ./bin/str_rm_substr
enter string: joy is joyful
enter substr: joy
result: ' is joyful'
A more complicated example:
$ ./bin/str_rm_substr
enter string: joy is joyful, joy is full of joy
enter substr: joy
result: ' is joyful, is full of '
There are many ways to write a function like this. You can use combinations of strtok() to tokenize a copy of your original string checking whether each token matches your substr to remove. You can inch-worm down your string using multiple loops to scan forward to find the first letter in your substr and then loop to see if it matches. You can also use combinations of strspn() and strcspn() to do the same inch-worm technique, letting those function handle looping for you. There are probably a 1/2-dozen or so valid approaches.
Look things over and let me know if you have questions.
Here
if(string[ij+1] == '\0' && string[ij+1] == ' '
you test if a character is both a nul and a space.
That will never be true. In order words, the whole if-statement is useless as it always takes the false path.
The problem is in this case if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n') and the counter decreasing. So after decreasing you never will get into this code:
if(counter == removeLen)
{
for(l=0; l<count; l++)
{
for(k=location; k<sLen; k++)
{
string[k] = string[k+1];
}
sLen--;
}
}
So remove this code:
if(count == removeLen)
{
if(string[ij+1] == '\0' && string[ij+1] == ' ' && string[ij+1] == '\n')
{
counter = count;
}
else
{
counter = count - 1;
}
}
And it will be work.
I want to read a string of numbers (only intigers) that I don't know and also I don't know how many of these numbers I will have to read. Each will be separated by whitespace. So waht is the best way to do it?
You don't have to write me a code or something, I just want to know what should I use.
Thank you
You can read character by character. Everytime you find number(character from 48 to 57), add to temporary string. When you have whitespace, try to parse created string. Then empty it. And continue it till the end of the big string.
I think that this might work
int main(){
char name[100];
printf("Insert numbers: ");
fgets(name, 100, stdin);
printf("Your numbers: %s\n", name);
return 0;
}
You have to read in a loop, skipping the blank spaces (see isspace(3)) and in an inner loop, while (isdigit(getchar())) (see isdigit(3))
I'll write some code (if you don't want to be spoiled out, don't read below until you are satisfied with your solution):
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
/* this macro calculates the number of elements of an array.
* you must be carefull to always pass an array (not a pointer)
* to effectively get the number of elements */
#define SIZE(arr) ((sizeof arr) / (sizeof arr[0]))
int main()
{
int n = 0;
int a[100];
/* this has to be an int (not a char) see getchar() manpage */
int last_char = 0;
/* while n < number of elements of array a */
while (n < SIZE(a)) {
/* read the character into last_char and check if it is
* a space, tab or newline */
while (isspace(last_char = getchar()))
continue;
/* last_char is not a space, it can be EOF, a minus sign,
* a digit, or something else (not a number) */
if (last_char == EOF)
break; /* exit the outer loop as we got no more input data */
int neg = (last_char == '-'); /* check for negative number */
if (neg) last_char = getchar(); /* advance */
/* check for digits */
if (isdigit(last_char)) {
/* digits are consecutive chars starting at '0'. We are
* assuming ASCII/ISO-LATIN-1/UTF-8 charset. This doesn't
* work with IBM charsets. */
int last_int = last_char - '0';
while (isdigit(last_char = getchar())) {
last_int *= 10; /* multiply by the numeration base */
last_int += last_char - '0'; /* see above */
}
/* we have a complete number, store it. */
if (n >= SIZE(a)) { /* no more space to store numbers */
fprintf(stderr,
"No space left on array a. MAX size is %d\n",
SIZE(a));
exit(EXIT_FAILURE);
}
if (neg) last_int = -last_int;
a[n++] = last_int;
}
/* next step is necessary, because we can be on a terminal and
* be able to continue reading after an EOF is detected. Above
* check is done after a new read to the input device. */
if (last_char == EOF)
break;
} /* while (n < SIZE(a) */
printf("Number list (%d elements):", n);
int i;
for (i = 0; i < n; i++) {
printf(" %d", a[i]);
}
printf("\n");
exit(EXIT_SUCCESS);
} /* main */
I am trying to enter a word, and get how many times the letters were typed.
Say my input is "hello"
my output would be: h = 1, e = 1 l = 2 etc.
I am very close to getting it right, but I have a small issue with this code:
#include <stdio.h>
#include <string.h>
void find_frequency(char s[], int count[]) {
int c = 0;
while (s[c] != '\0') {
if (s[c] >= 'a' && s[c] <= 'z' )
count[s[c]-'a']++;
c++;
}
}
int main()
{
char string[100];
int c, count[26] = {0};
printf("Input a string\n");
gets(string);
find_frequency(string, count);
printf("Character Count\n");
for (c = 0 ; c < 26 ; c++)
if(count[c] > 0)
printf("%c : %d\n", c + 'a', count[c]);
return 0;
}
This code does half of the job, but not all.
It's output is in alphabetical order. How can i change it to give me an output of just the chararray that is input?
As Ry- suggested in this comment you could iterate back over the original string and use the chars as indices into your frequency table. Something like the following:
int len_string = strlen(string);
for (c=0; c<len_string; c++) {
char ch = string[c];
printf("%c: %d, ", ch, count[ch-'a']);
}
This won't completely match your expected output, since this code will output l: 2 twice, but that raises the question:
What is your expected output when you have a string like abba? a:2, b:2? a:1, b:2, a:1? a: 2, b:2, a:2? It's hard to help when you ask such an ambiguous question.
#include <stdio.h>
#include <string.h>
size_t ASCIIfreq[256];
void CountASCII(void *buff, size_t size)
{
unsigned char *charsptr = buff;
memset(ASCIIfreq, 0, sizeof(ASCIIfreq));
while(size--)
{
ASCIIfreq[*charsptr++]++;
}
}
void print(int printall)
{
for(size_t index = 0; index < 256; index++)
{
if(ASCIIfreq[index] || printall)
{
printf("The %03zu (0x%02zx) ASCII - '%c' has occured in the buffer %zu time%c\n",
index, index, (index > 32 && index < 127) ? (char)index : ' ',
ASCIIfreq[index], ASCIIfreq[index] == 1 ? ' ' : 's');
}
}
}
int main()
{
char teststring[] = "i am trying to enter a word, and get how many times the letters were typed. Say my input is \"hello\" my output would be: h = 1, e = 1 l = 2 etc.I am very close to getting it right, but i have a small issue with this code";
CountASCII(teststring, sizeof(teststring));
print(0);
return 0;
}
It's not clear what you mean by:
How can i change it to give me an output of just the chararray that is input?
Because that's exactly what you're doing in any case: Inputting a char array to the function; which is updated with numbers alphabetically; and later output as is.
So I'm guessing that you want to output the counts in the same order that each char was first encountered?
Solution
This will require a bit more work. You could keep a second array tracking the the order each character is encountered within find_frequency. But then that simple clean function starts doing too much.
So consider rather tweaking how you do the output:
void output_frequency(char s[], int count[]) {
int c = 0;
//loop s for the output
while (s[c] != '\0') {
if (s[c] >= 'a' && s[c] <= 'z' ) {
//found a character, report the count only if not reported before
if (count[s[c]-'a'] > 0) {
printf("%c : %d\n", s[c], count[s[c] - 'a']);
count[s[c]-'a'] = 0; //so you don't report this char again
}
}
c++;
}
}
If you are attempting to get an in-order count instead of a count in alphabetical order, you simply need to coordinate the indexes of your count array with the order of characters in your input buffer. To do that, simply loop over all characters in your input buffer and make a second pass counting the number of times the current character occurs. This will give you an in-order count of the number of times each character occurs, e.g.
#include <stdio.h>
#include <string.h>
#define COUNT 128
#define MAXC 1024
int main (void) {
char buf[MAXC] = ""; /* buffer to hold input */
int count[COUNT] = {0}; /* array holding inorder count */
fputs ("enter string: ", stdout); /* prompt for input */
if (!fgets (buf, MAXC, stdin)) { /* read line into buf & validate */
fputs ("error: EOF, no valid input.\n", stderr);
return 1;
}
/* loop over each character not '\n' */
for (int i = 0; buf[i] && buf[i] != '\n'; i++) {
char *p = buf; /* pointer to buf */
size_t off = 0; /* offset from start of buf */
while ((p = strchr (buf + off, buf[i]))) { /* find char buf[i] */
count[i]++; /* increment corresponding index in count */
off = p - buf + 1; /* offset is one past current char */
}
}
for (int i = 0; count[i]; i++) /* output inorder character count */
printf (i ? ", %c: %d" : "%c: %d", buf[i], count[i]);
putchar ('\n'); /* tidy up with new line */
return 0;
}
(note: strchr is used for convenience to simply find the next occurrence of the current character within the string and then off (offset) is used to start the search with the following character until no other matches in the string are found. You can simply use an additional loop over the characters in the buffer if you like.)
Example Use/Output
$ /bin/charcnt_inorder
enter string: hello
h: 1, e: 1, l: 2, l: 2, o: 1
However, this does recount each character and give the count again if the character is duplicated, (e.g. l: 2, l: 2 for each 'l'). Now it is unclear from:
"my output would be: h = 1, e = 1 l = 2 etc."
what you intended in that regard, but with just a little additional effort, you can use a separate index and a separate array to store the first instance of each character (in say a chars[] array) along with the count of each in your count[] array and preserve your inorder count while eliminating duplicate characters. The changes needed are shown below:
#include <stdio.h>
#include <string.h>
#define COUNT 128
#define MAXC 1024
int main (void) {
char buf[MAXC] = "",
chars[COUNT] = ""; /* array to hold inorder chars */
int count[COUNT] = {0};
size_t cdx = 0; /* add count index 'cdx' */
fputs ("enter string: ", stdout);
if (!fgets (buf, MAXC, stdin)) {
fputs ("error: EOF, no valid input.\n", stderr);
return 1;
}
for (int i = 0; buf[i] && buf[i] != '\n'; i++) {
char *p = buf;
size_t off = 0;
chars[cdx] = buf[i]; /* store in chars array */
if (i) { /* if past 1st char */
int n = i;
while (n--) /* simply check all before */
if (buf[n] == buf[i]) /* if matches current */
goto next; /* bail and get next char */
}
while ((p = strchr (buf + off, buf[i]))) {
count[cdx]++; /* increment count at index */
off = p - buf + 1;
}
cdx++; /* increment count index */
next:; /* goto label to jump to */
}
for (int i = 0; count[i]; i++)
printf (i ? ", %c: %d" : "%c: %d", chars[i], count[i]);
putchar ('\n');
return 0;
}
Example Use/Output
$ /bin/charcnt_inorder2
enter string: hello
h: 1, e: 1, l: 2, o: 1
or
$ ./bin/charcnt_inorder2
enter string: amarillo
a: 2, m: 1, r: 1, i: 1, l: 2, o: 1
Now your 'l' is only reported once with the correct count.
Note, in each example you should do additional validation to insure the entire input fit within your buffer, etc... The count (and chars) array were sized at 128 to cover the entire range of ASCII values. Don't skimp on buffer size. If you explicitly limit your input to UPPERcase or lowercase -- then you can limit your count size to 26, otherwise you need to consider the additional characters and punctuation that will be encountered. The same applies to your input buffer. If you anticipate you max input would be 500 chars, double it (generally to next available power of two, no real requirement for powers of two, but you are likely to see it that way).
Bottom line, I'd rather be 10,000 characters too long that one character too short... leading to Undefined Behavior.
Lastly, as mentioned in my comment never, never, never use gets. It is so insecure it has been removed from the C standard library in C11. Use fgets or POSIX getline instead.
Look things over and let me know if you have further questions.
Here's my code:
include <stdio.h>
int main()
{
char str[1000], ch;
int i, frequency = 0;
printf("Enter a string: ");
gets(str);
printf("Enter a character to find the frequency: ");
scanf("%c",&ch);
for(i = 0; str[i] != '\0'; ++i)
{
if(ch == str[i])
++frequency;
}
printf("Frequency of %c = %d", ch, frequency);
return 0;
I figured that the frequency of characters code I came up with is similar. How to implement the character which appears more / less often in standard input or text file?
Also, should I use StreamReader sr = new StreamReader("example.txt") for reading normal text files for this code?
EDIT: Have to use Switch /M for most often and /L for least often.
That's a good start...
include <stdio.h>
int main()
{
char str[1000], ch,lookup_Chars[256];
int i, frequency = 0;
char counter;
printf("Enter a string: ");
gets(str);
printf("Enter a character to find the frequency: ");
scanf("%c",&ch);
for(i = 0; str[i] != '\0'; ++i)
{
lookup_Chars[str[i]]++;
}
for(counter = 0; counter<sizeof(lookup_Chars); counter++)
{
printf("Frequency of %c = %d", counter, lookup_Chars[counter]);
}
return 0;
Never, never, never use gets. It is so insecure and so susceptible to buffer overrun, it has been removed from the C standard library. Use fgets instead, just be aware that fgets will read and include the trailing '\n' in the buffer it fills (just as all legitimate line oriented input functions do, such as POSIX getline). This prevents leaving a '\n' unread in the input buffer (e.g. stdin) following each user input.
You risk Undefined Behavior because you do not validate the contents of str in any way and then you fail to validate the return of scanf to insure a character was read. (the user could cancel input in either case by generating an EOF with Ctrl+d on *nix systems of with Ctrl+z on windoze).
Further, you must understand that scanf will leave characters in the input buffer (as will fgets if the line is longer than the amount of storage you have allocated). That is one of the most common pitfalls new C programmers fall victim to. (failing to validate a complete line of input was read and failing to handle characters that remain in the input buffer)
When taking user input with fgets (which is recommended), since it reads and includes the '\n' in the buffer it fills, you simply check the length of the buffer read with strlen and then make sure the last character is a '\n'.
scanf is full of pitfalls when used for user input. While it can be used, if used correctly with it's return validated and any remaining characters emptied from stdin before your next call to scanf, you have to approach it use that way. In your case ch is the last input for your file, but try taking input for ch before reading the string and see what happens...
Putting it altogether, and adding validations for both str and ch, and adding additional comments in-line below, you could do something similar to the following:
#include <stdio.h>
#include <string.h>
#define MAXS 1024 /* if you need a constant, define one */
int main (void) {
int frequency = 0;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch;
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
if (*p++ == ch) /* compare to ch and increment to next char */
frequency++; /* increment frequency if they are equal */
printf ("\nFrequency of %c = %d\n", ch, frequency);
return 0;
}
(note: you could declare char ch[3] = ""; and use fgets to read fgets (ch, sizeof ch, stdin) and then simply compare if (*p++ == *ch) to prevent leaving the '\n' in stdin (but you would still need to validate that it was the final character read, and if not manually empty stdin))
Exammple Use/Output
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: a
Frequency of a = 10
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: p
Frequency of p = 2
$ ./bin/freqofc
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: z
Frequency of z = 0
Look things over, think about the validations that were made, and let me know if you have any further questions.
Using a Frequency Array to Capture Count of all Chars
Using a frequency array allows you to capture the frequency of all characters (or the independent count of any element of a set). Essentially, you use an array initialized to zero with one element for each member of the set you want to count the frequency of each occurrence. Since there are 128 ASCII Characters, you can simply use an array of 128 elements, e.g. int frequency[128] = {0};.
If you look at the link provided, you see the ASCII value of each character corresponds to a value between 0-127, so when looping over each character in the input string, if you increment the array index that corresponds to the character, you end up with the total count for each character in its corresponding element. For example, if p is a pointer to the beginning of str, then you can loop over each character in the string capturing their frequency in the frequency[*p] element of the array:
while (*p != '\n') {
frequency[*p]++; /* increments element corresponding to char *p */
p++; /* note: cast to (int)*p intentional omitted */
}
Now that you have the frequency for every character stored in the frequency, you can simply loop over the elements you are concerned about to determine max/min, etc... Note: the normal printable characters begin with 'space' (ASCII 32, or hex 0x20) and end with '~' (ASCII 126 or hex 0x7e`). So just limit your check of values to the printable range, e.g.
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i]; /* save least frequent count */
minc = i; /* save least frequent char */
}
if (frequency[i] > max) { /* just find max */
max = frequency[i]; /* save same for max */
maxc = i;
}
}
(note: you can further micro-divide ranges for only lowercase, uppercase, digits, etc..)
Putting that altogether, you can do something similar to the following to report the number of occurrence if the wanted char, the max occurring char, the min occurring char (and then summarize by dumping the frequency of all chars):
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXS 1024 /* if you need a constant, define one */
#define NASCII 128 /* number of ASCII chars (includes non-printing) */
int main (void) {
int frequency[NASCII] = {0},
max = INT_MIN,
min = INT_MAX;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch,
minc = 0,
maxc = 0;
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
frequency[(int)*p++]++; /* increment element representing ch */
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i]; /* save least frequent count */
minc = i; /* save least frequent char */
}
if (frequency[i] > max) { /* just find max */
max = frequency[i]; /* save same for max */
maxc = i;
}
}
/* ouput requested char freq, and max/min chars */
printf ("\nFrequency of %c = %d\n"
"least frequent occurrence: %c = %d\n"
" most frequent occurrence: %c = %d\n\n",
ch, frequency[(int)ch], minc, min, maxc, max);
/* output frequency of all printable chars */
printf ("frequency of all printable characters:\n");
for (int i = ' '; i < '~'; i++)
if (frequency[i])
printf (" '%c' : %d\n", i, frequency[i]);
return 0;
}
Exammple Use/Output
$ ./bin/freqofc2
Enter a string: a man a plan a canal panama
Enter a character to find the frequency: m
Frequency of m = 2
least frequent occurrence: c = 1
most frequent occurrence: a = 10
frequency of all printable characters:
' ' : 6
'a' : 10
'c' : 1
'l' : 2
'm' : 2
'n' : 4
'p' : 2
Adding /L or /M Switches for Least/Max Occurrences
To add command line switches, for a minimum number in a known order, you can simply use the allowable argument count and argument vector parameters to main(), e.g. int main (int argc, char **argv). Example:
int main (int argc, char **argv) {
...
/* validate "/L" or "/M" provided as an argument */
if (argc != 2 || (argv[1][1] != 'L' && argv[1][1] != 'M')) {
fprintf (stderr, "error: insufficient input, req'd /M or /L.\n");
return 1;
}
To test and output either the least or minimum, you simply test which character is present and act accordingly, e.g.
...
if (argv[1][1] == 'L') /* output requested lease or max */
printf ("requested least frequent occurrence: %c = %d\n\n",
minc, min);
else
printf ("requested most frequent occurrence: %c = %d\n\n",
maxc, max);
Putting that together an a complete example would be:
#include <stdio.h>
#include <string.h>
#include <limits.h>
#define MAXS 1024 /* if you need a constant, define one */
#define NASCII 128 /* number of ASCII chars (includes non-printing) */
int main (int argc, char **argv) {
int frequency[NASCII] = {0},
max = INT_MIN,
min = INT_MAX;
char str[MAXS] = "",
*p = str, /* pointer to str */
ch,
minc = 0,
maxc = 0;
/* validate "/L" or "/M" provided as an argument */
if (argc != 2 || (argv[1][1] != 'L' && argv[1][1] != 'M')) {
fprintf (stderr, "error: insufficient input, req'd /M or /L.\n");
return 1;
}
printf ("Enter a string: ");
if (fgets (str, MAXS, stdin)) { /* validate input received */
size_t len = strlen (str); /* get length of str */
if (len && str[len - 1] != '\n') { /* validate all input read */
fprintf (stderr, "error: line exceeds %d chars.\n", MAXS-2);
return 1;
}
}
else { /* if fgets failed - user generated EOF to cancel */
fprintf (stderr, "error: user canceled input (EOF).\n");
return 1;
}
printf ("Enter a character to find the frequency: ");
if (scanf ("%c", &ch) != 1) { /* note: chars will remain in stdin */
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
while (*p != '\n') /* just use a pointer to str */
frequency[(int)*p++]++; /* increment element representing ch */
/* loop over printable characters (see ASCII Chart), for max/min */
for (int i = ' '; i <= '~'; i++) {
/* require a frequency of at least 1 for min */
if (frequency[i] && frequency[i] < min) {
min = frequency[i];
minc = i;
}
if (frequency[i] > max) { /* just find max */
max = frequency[i];
maxc = i;
}
}
/* ouput requested char freq, and max/min chars */
printf ("\nFrequency of %c = %d\n"
"least frequent occurrence: %c = %d\n"
" most frequent occurrence: %c = %d\n\n",
ch, frequency[(int)ch], minc, min, maxc, max);
if (argv[1][1] == 'L') /* output requested lease or max */
printf ("requested least frequent occurrence: %c = %d\n\n",
minc, min);
else
printf ("requested most frequent occurrence: %c = %d\n\n",
maxc, max);
/* output frequency of all printable chars */
printf ("frequency of all printable characters:\n");
for (int i = ' '; i < '~'; i++)
if (frequency[i])
printf (" '%c' : %d\n", i, frequency[i]);
return 0;
}
Let me know if you have any questions.