I am an experienced Python developer learning C. I solved this assignement:
Write a program that reads a word and some sentences from stdin. The word is separated by space from a sentences. The sentences are separated by one of the following characters:
.!?
. Words in sentences are separated by any char for which one of library functionsisspace
andispunct
returns nonzero result.Your program should output average usage of the word in the sentences (a number of matched words divided by a number of sentences).
Example input:
two one four two seven eight two four two. two, one, nine, two, two, three, two!
Example expected output:
3.5
Explanation: The word is "two". Then we have two sentences: "one four two seven eight two four two." and "two, one, nine, two, two, three, two!". The word occurs 7 times, so result is 7/2 = 3.5.
Here is my solution:
# include <ctype.h>
# include <stdbool.h>
# include <stdio.h>
# include <stdlib.h>
# include <unistd.h>
void check_malloc_result(void* malloc_result) {
if (!malloc_result) {
perror("malloc failed");
exit(EXIT_FAILURE);
}
}
int main(void) {
char stdin_ch;
size_t buff_size = 8;
size_t word_len = 0;
bool reading_word = false;
char* word = malloc(buff_size);
check_malloc_result(word);
// reading the word
while(read(STDIN_FILENO, &stdin_ch, 1) > 0) {
if (isspace(stdin_ch)) {
if (reading_word) {
break;
} else {
continue;
}
}
reading_word = true;
++word_len;
if (word_len > buff_size) {
buff_size *= 2;
word = realloc(word, buff_size);
check_malloc_result(word);
}
word[word_len - 1] = stdin_ch;
}
// counting matches
size_t sen_count = 0, matched_count = 0, cur_word_pos = 0;
bool matching = true;
reading_word = false;
while(word_len && read(STDIN_FILENO, &stdin_ch, 1) > 0) {
if (ispunct(stdin_ch) || isspace(stdin_ch)) {
if (reading_word) {
// end of a word
if (matching) {
++matched_count;
}
if (stdin_ch == '.' || stdin_ch == '?' || stdin_ch == '!') {
++sen_count;
}
reading_word = false;
matching = true;
cur_word_pos = 0;
}
continue;
}
reading_word = true;
if (matching) {
if (cur_word_pos >= word_len || word[cur_word_pos] != stdin_ch) {
matching = false;
}
++cur_word_pos;
}
}
free(word);
// printing the result
double result = 0.0;
if (sen_count) {
result = (double)matched_count / (double)sen_count;
}
printf("%f", result);
return EXIT_SUCCESS;
}
Some notes:
- I don't want to use fixed max length for the word so I use dynamic allocation here.
- The reading of sentences is done using two boolean flags.
reading_word
means are actualy reading a word, and if it's false, we are somewhere in between. It's used for dealing with consecutive separators.matching
becomes false when we know that current scanned word doesn't match, so we just scan to the end of the word, not checking anything.
What could be better in this code? Is it "idiomatic" C, or is it obvious that it's written by someone without much language experience?