简体   繁体   中英

I want to read from text file that displays which character appears most or least often (keyboard or text file) ? :)

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 (eg 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, eg 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, eg

    /* 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() , eg 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, eg

    ...
    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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM