0

I'm trying to write a program that takes a file and a string by using standard POSIX functions, program counts all the characters in file which the string contains.

For example if the user writes:

count.exe x.txt abcd

The program calculates the number of each character: a, b, c, d in file x.txt

Sample message:

Number of 'a' characters in 'x.txt' file is: 4
Number of 'b' characters in 'x.txt' file is: 9
Number of 'c' characters in 'x.txt' file is: 7
Number of 'd' characters in 'x.txt' file is: 0

The code that I got so far:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

#define BUFSIZE     1024


void exit_sys(const char* msg)
{
    perror(msg);
    exit(EXIT_FAILURE);
}

void exit_fail(const char* msg)
{
    fprintf(stderr, "%s\n", msg);
    exit(EXIT_FAILURE);
}

int get_count(char* p, size_t size, char c)
{
    int count = 0;
    size_t i;

    for (i = 0; i < size; ++i)
        if (p[i] == c)
            ++count;

    return count;
}


void run_count_characters_application(int argc, char** argv)
{
    int fd;
    char c;
    char buf[BUFSIZE];
    int n;
    int count;

    if (argc != 3)
        exit_fail("usage: ./mycounter file character");

    if (strlen(argv[2]) < 0)
        exit_fail("You have to give at least one character");

    c = argv[2][0];

    if ((fd = open(argv[1], O_RDONLY)) < 0)
        exit_sys("open");

    count = 0;


    while ((n = read(fd, buf, BUFSIZE)) > 0)
        count += get_count(buf, n, c);

    if (n < 0)
        exit_sys("read");


    printf("Count:%d\n", count);

    close(fd);
}

int main(int argc, char** argv)
{
    run_count_characters_application(argc, argv);

    return 0;
}

The problem with what I got so far in this code is that it only counts one character (only the first character), I want to know how to make it read and count the other characters that I write in the command, thank you in advance :)

  • 2
    You are doing `c = argv[2][0];` by which only the ASCII value of `c` is passed, not the rest of the characters – Inian May 23 '20 at 19:10
  • 3
    you have to keep several counters, one for each char in the argument string. you could store them inside an `unsigned int counter[UCHAR_MAX + 1]` and update and print just the ones you are interested in (e.g. `counter['a']++`). this will works just for ascii chars though... – MarcoLucidi May 23 '20 at 19:18
  • 1
    @Inian how do I fix that ? –  May 23 '20 at 19:28
  • 1
    @MarcoLucidi can you explain it by example because I'm very knew at C language and I'm not fully understanding on how to do it. –  May 23 '20 at 19:30

1 Answers1

0

since you asked for an example in the comments:

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void die(const char *reason)
{
        fprintf(stderr, "%s\n", reason);
        exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
        if (argc != 3)
                die("usage: ./count file characters");

        FILE *f = fopen(argv[1], "r");
        if (f == NULL)
                die("unable to open file");

        unsigned int counter[UCHAR_MAX + 1] = { 0 };
        int c;
        while ((c = fgetc(f)) != EOF)
                counter[c]++;
        fclose(f);

        size_t len = strlen(argv[2]);
        for (unsigned int i = 0; i < len; i++) {
                char c = argv[2][i];
                unsigned int count = counter[c];
                printf("Number of '%c' characters in '%s' file is: %u\n", c, argv[1], count);
        }
}

$ cc count.c -o count
$ echo "bbaaafff" > test.txt
$ ./count test.txt afm
Number of 'a' characters in 'test.txt' file is: 3
Number of 'f' characters in 'test.txt' file is: 3
Number of 'm' characters in 'test.txt' file is: 0
$

the function fgetc()

return the character read as an unsigned char cast to an int or EOF on end of file or error.

there are UCHAR_MAX + 1 possible values that a unsigned char can store (typically 0 to 255) so I made an array that can be indexed by these values (counter) to store ours counts. think of it as a "map" from other languages that maps characters to their count.

then at the end, I loop over the characters from the input string and print their count.

as already mentioned in the comments, this works properly only for ASCII characters.

MarcoLucidi
  • 2,007
  • 1
  • 5
  • 8
  • how do I use **open();** instead of **fopen();** and for **fclose();** can I just use **close();**?, because I'm trying to understand what is the difference between these and others like **fread();** > **read();** or **fwrite();** > **write();**, **fgetc();** > **getc();**. –  May 25 '20 at 09:00
  • 1
    @ArjanitKotorri long story short: `open()`, `read()`, `close()`... are unix system calls while `fopen()`, `fread()`, `fclose()`... are c library wrapper on those system calls, they provide you buffered I/O and allow you to write more portable code. see: [C fopen vs open](https://stackoverflow.com/questions/1658476/c-fopen-vs-open) – MarcoLucidi May 25 '20 at 11:16
  • Can we say that **open():**, **read();**, **close();**... are POSIX functions and **fopen();**, **fread();**, **fclose();** are standard C functions also would it work if instead of ```FILE *f = fopen(argv[1], "r");``` I did ```FILE *f = open(argv[1], O_RDONLY);``` ? –  May 25 '20 at 11:52
  • 1
    @ArjanitKotorri `fopen()` and friends are also defined in POSIX so they are "POSIX functions" too (see: [`fopen`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fopen.html)). the last statement will not work: `open()` returns a file descriptor (an `int`) while `fopen()` returns a [`FILE`](https://en.cppreference.com/w/c/io). – MarcoLucidi May 25 '20 at 16:50