Scan and swap string values with regex in ANSI C - c

I want to transform a given input in my c program, for example:
foo_bar_something-like_this
into this:
thissomethingbarfoolike
Explanation:
Every time I get a _, the following text up to, but not including, the next _ or - (or the end of the line) needs to go to the beginning (and the preceding _ needs to be removed). Every time I get a -, the following text up to, but not including, the next _ or - (or the end of the line) needs to be appended to the end (with the - removed).
If possible, I would like to use regular expressions in order to achieve this. If there is a way to do this directly from stdin, it would be optimal.
Note that it is not necessary to do it in a single regular expression. I can do some kind of loop to do this. In this case I believe I would have to capture the data in a variable first and then do my algorithm.
I have to do this operation for every line in my input, each of which ends with \n.
EDIT: I had already written a code for this without using anything related to regex, besides I should have posted it in the first place, my apologies. I know scanf should not be used to prevent buffer overflow, but the strings are already validated before being used in the program. The code is the following:
#include <stdio.h>
#include <stdlib.h>
#define MAX_LENGTH 100001 //A fixed maximum amount of characters per line
int main(){
char c=0;
/*
*home: 1 (append to the start), 0 (append to the end)
*str: array of words appended to the begining
*strlen: length of str
*line: string of words appended to the end
*linelen: length of line
*word: word between a combination of symbols - and _
*wordlen: length of the actual word
*/
int home,strlen,linelen,wordlen;
char **str,*line,*word;
str=(char**)malloc(MAX_LENGTH*sizeof(char*));
while(c!=EOF && scanf("%c",&c)!=EOF){
line=(char*)malloc(MAX_LENGTH);
word=(char*)malloc(MAX_LENGTH);
line[0]=word[0]='\0';
home=strlen=linelen=wordlen=0;
while(c!='\n'){
if(c=='-'){ //put word in str and restart word to '\0'
home=1;
str[strlen++]=word;
word=(char*)malloc(MAX_LENGTH);
wordlen=0;
word[0]='\0';
}else if(c=='_'){ //put word in str and restart word to '\0'
home=0;
str[strlen++]=word;
word=(char*)malloc(MAX_LENGTH);
wordlen=0;
word[0]='\0';
}else if(home){ //append the c to word
word[wordlen++]=c;
word[wordlen]='\0';
}else{ //append c to line
line[linelen++]=c;
line[linelen]='\0';
}
scanf("%c",&c); //scan the next character
}
printf("%s",word); //print the last word
free(word);
while(strlen--){ //print each word stored in the array
printf("%s",str[strlen]);
free(str[strlen]);
}
printf("%s\n",line); //print the text appended to the end
free(line);
}
return 0;
}

I do not think regex can do what you are asking for, so I wrote a simple state machine solution in C.
//
//Discription: This Program takes a string of character input, and parses it
//using underscore and hyphen as queue to either send data to
//the begining or end of the output.
//
//Date: 11/18/2017
//
//Author: Elizabeth Harasymiw
//
#include <stdio.h>
#include <string.h>
#define MAX_SIZE 100
typedef enum{ AppendEnd, AppendBegin } State; //Used to track either writeing to begining or end of output
int main(int argc,char**argv){
char ch; //Used to hold the character currently looking at
State state=AppendEnd; //creates the State
char Buffer[MAX_SIZE]={}; //Current Ouput
char Word[MAX_SIZE]={}; //Pending data to the Buffer
char *c; //Used to index and clear Word
while((ch = getc(stdin)) != EOF){
if(ch=='\n')continue;
switch(state){
case AppendEnd:
if( ch == '-' )
break;
if( ch == '_'){
state = AppendBegin; //Change State
strcat(Buffer, Word); //Add Word to end of Output
for(c=Word;*c;c++)*c=0; //Clear Word
break;
}
{
int postion = -1;
while(Word[++postion]); //Find end of Word
Word[postion] = ch; //Add Character to end of Word
}
break;
case AppendBegin:
if( ch == '-' ){
state = AppendEnd; //Change State
strcat(Word, Buffer); //Add Output to end of Word
strcpy(Buffer, Word); //Move Output from Word back to Output
for(c=Word;*c;c++)*c=0; //Clear Word
break;
}
if( ch == '_'){
strcat(Word, Buffer); //Add Output to end of Word
strcpy(Buffer, Word); //Move Output from Word back to Output
for(c=Word;*c;c++)*c=0; //Clear Word
break;
}
{
int postion = -1;
while(Word[++postion]); //Find end of Word
Word[postion] = ch; //Add Character to end of Word
}
break;
}
}
switch(state){ //Finish adding the Last Word Buffer to Output
case AppendEnd:
strcat(Buffer, Word); //Add Word to end of Output
break;
case AppendBegin:
strcat(Word, Buffer); //Add Output to end of Word
strcpy(Buffer, Word); //Move Output from Word back to Output
break;
}
printf("%s\n", Buffer);
}

This can be done with regexes using loops, assuming you aren't strictly restricted to ANSI. The following uses PCRE.
(Note that this answer deliberately does not show the C code. It is only meant to guide the OP by showing a possible technique for using regexes, as it is not obvious how to do so.)
Method A
Uses two different regexes.
Part 1/2 (Demo)
Regex: ([^_\n]*)_([^_\n]*)(_.*)? Substitution: $2--$1$3
This moves the text following the next underscore to the beginning, appending -- to it. It also removes the underscore. You need to repeat this substitution in a loop until no more matches are found.
For your example, this leads to the following string:
this--something-like--bar--foo
Part 2/2 (Demo):
Regex: (.*)(?<!-)-(?!-)(\w+)(.*) Substitution: $1$3--$2
This moves the text following the next single hyphen to the end, prepending -- to it. It also removes the hyphen. You need to repeat this substitution in a loop until no more matches are found.
For your example, this leads to the following string:
this--something--bar--foo--like
Remove the hyphens from the string to get your result.
Note that the first regex can be simplified to the following and will still work:
([^_]*)_([^_]*)(_.*)?
The \ns were only required to show the intermediate loop results in the demos.
The following are the reasons for using -- as a new separator:
A separator is required so that the regex in part 2 can find the correct end of hyphen prefixed text;
A underscore can't be used as it would interfere with the regex in part 1 causing an infinite loop;
A hyphen can't be used as it would cause the regex in part 2 to find extraneous text;
Although any single character delimiter which can never exist in the input would work and lead to a simpler part 2 regexp, -- is one of the delimiters which allows any and every character* in the input.
\n is actually the perfect * delimiter, but can't be used in this answer as it would not allow the demo to show the intermediate results. (Hint: it should be the actual delimiter used by you.)
Method B
Combines the two regexes.
(Demo)
Regex: ([^_\n]*)_([^_\n]*)(_.*)?|(.*)(?<!-)-(?!-)(\w+)(.*) Substitution: $2--$1$3$4$6--$5
For your example, this leads to the following string:
----this------something--bar--foo----like
As before, remove all the hyphens from the string to get your result.
Also as before, the regex can be simplified to the following and will still work:
([^_]*)_([^_]*)(_.*)?|(.*)(?<!-)-(?!-)(\w+)(.*)
This combined regex works because capturing groups 1,2 & 3 are mutually exclusive to groups 4, 5 & 6. There is a side effect of extra hyphens, however.
Caveat:
* Using -- as a delimiter fails if the input contains consecutive hyphens. All the other "good" delimiters have a similar failure edge case. Only \n is guaranteed not to exist in the input and thus is failsafe.

Related

Understanding this program which prints a sentence in reverse but keeps the words unchanged

I don't quite understand this program. I don't understand what is happening in the for loop. Can someone explain to me in simple words. And the site also didn't explain it well-enough. This is the link to the site. https://www.geeksforgeeks.org/print-words-string-reverse-order/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void printReverse(char str[])
{
int length = strlen(str);
FILE *fptr;
if((fptr=fopen("Question1.txt","w"))==NULL)
{
printf("Invalid file");
exit(0);
}
int i;
for (i = length - 1; i >= 0; i--) {
if (str[i] == ' ')
{
str[i] = '\0';
printf("%s ", &(str[i]) + 1);
fprintf(fptr,"%s ", &(str[i]) + 1);
}
}
fprintf(fptr,"%s",str);
printf("%s.", str);
fclose(fptr);
}
int main()
{
char str[1000];
//clrscr();
printf("Enter string: ");
scanf("%[^\n]s", str);
printReverse(str);
//getch();
return 0;
}
In the for loop, why put &(str[i])+1? And also in printf("%s.", str)--this only has the first word; how?
Okay, let's see if I can help. I'll go through the code carefully.
I suspect you already understand this. It's just a method call.
void printReverse(char str[])
{
strlen is a standard method that returns the length of a null-terminated string. That means that str might contain Hello (5 characters), but there's one more byte with a 0 in it, which is how C has always marked the end of the string. In this case, str itself takes 6 bytes, but length will be 5.
int length = strlen(str);
This is how you open a file in C. C++ has better ways. The file is written for writing.
FILE *fptr;
if((fptr=fopen("Question1.txt","w"))==NULL)
{
printf("Invalid file");
exit(0);
}
Here's your for-loop. Let's assume str contains Hello, so length is 5, but the indexes into string are str[0..4]. C uses the index as "offset from the beginning", so the first element is 0, not 1. Thus, when this loop starts, str[i] == o (using Hello as our example string). We then loop, decrementing i each time. Once i goes below 0, the loop ends.
int i;
for (i = length - 1; i >= 0; i--) {
Okay, remember we're printing the words in normal order, but the words themselves are in reverse order. So this looks for a space -- between words. So if we use Hello there as our input text, this if-statement is true when i is pointing to the space between the two words.
Now here's the trick. Remember what I said earlier about null-terminated strings? What this does is to step on that space and replace it with a 0. That makes the rest of this magic work.
if (str[i] == ' ')
{
str[i] = '\0';
And here's the magic. Now, this is a strange way to do it. I would have done it with &str[i+1], but this works. What this is doing is saying "Print the string that begins after the space we just clobbered." We do it to the terminal and the file.
printf("%s ", &(str[i]) + 1);
fprintf(fptr,"%s ", &(str[i]) + 1);
}
}
This writes the produced rearranged string to the file that was opened as well as to your terminal then makes sure the file is closed.
fprintf(fptr,"%s",str);
printf("%s.", str);
fclose(fptr);
}
This all works because we step on the spaces with a zero. For Hello world, we:
Start from the tail
Find the space and stick a zero in it
Print world
Keep backing up to the end of the data.
Drop out of the for-loop and print whatever is left: Hello
Answer to your specific questions
In the for loop why put? &(str[i])+1?
&str[i] is the address of the character at index i where a space has been replaced with a NUL character. With +1 you get the address of the character after it, i.e. the beginning of the word that follows the space that was just replaced. (In case of double spaces this would result in an empty string.)
And also in printf("%s.", str); this only has the first word how?
Assuming the first word is not preceded by a space, the loop will not print it.
This printf("%s.", str); will print the string from the beginning until the first NUL character that replaces a former space character, hence resulting in the first word.
Additional question from comment
So... for example if I input Hello World does the W in that get the index 0?
The W is at index 6. (H is 0, e is 1 etc.)
When i has been counted down to 5, the space at this position will be replaced with a NUL ('\0') character, and it will print the remaining string from the W up to the end of the string which is also marked by a NUL character. (As defined by the C standard.)
And what if the character is not a NULL character? Then it won't go execute if right? It'll just increment i again till it encounters another NULL right?
I don't fully understand these questions. In case there was no NUL character at the end of the string printf would read past the end of the string leading to undefined behavior.
In case of an input string Hello World and Universe", all spaces after Worldwould have been replaced with NUL characters before, so when the program reaches the position of the space beforeWorld`, the string will be
Hello World\0and\0Universe\0
before the replacement and
Hello\0World\0and\0Universe\0
after the replacement.

Stdin + Dictionary Text Replacement Tool -- Debugging

I'm working on a project in which I have two main files. Essentially, the program reads in a text file defining a dictionary with key-value mappings. Each key has a unique value and the file is formatted like this where each key-value pair is on its own line:
ipsum i%##!
fubar fubar
IpSum XXXXX24
Ipsum YYYYY211
Then the program reads in input from stdin, and if any of the "words" match the keys in the dictionary file, they get replaced with the value. There is a slight thing about upper and lower cases -- this is the order of "match priority"
The exact word is in the replacement set
The word with all but the first character converted to lower case is in the replacement set
The word converted completely to lower case is in the replacement set
Meaning if the exact word is in the dictionary, it gets replaced, but if not the next possibility (2) is checked and so on...
My program passes the basic cases we were provided but then the terminal shows
that the output vs reference binary files differ.
I went into both files (not c files, but binary files), and one was super long with tons of numbers and the other just had a line of random characters. So that didn't really help. I also reviewed my code and made some small tests but it seems okay? A friend recommended I make sure I'm accounting for the null operator in processInput() and I already was (or at least I think so, correct me if I'm wrong). I also converted getchar() to an int to properly check for EOF, and allocated extra space for the char array. I also tried vimdiff and got more confused. I would love some help debugging this, please! I've been at it all day and I'm very confused.
There are multiple issues in the processInput() function:
the loop should not stop when the byte read is 0, you should process the full input with:
while ((ch = getchar()) != EOF)
the test for EOF should actually be done differently so the last word of the file gets a chance to be handled if it occurs exactly at the end of the file.
the cast in isalnum((char)ch) is incorrect: you should pass ch directly to isalnum. Casting as char is actually counterproductive because it will turn byte values beyond CHAR_MAX to negative values for which isalnum() has undefined behavior.
the test if(ind >= cap) is too loose: if word contains cap characters, setting the null terminator at word[ind] will write beyond the end of the array. Change the test to if (cap - ind < 2) to allow for a byte and a null terminator at all times.
you should check that there is at least one character in the word to avoid calling checkData() with an empty string.
char key[ind + 1]; is useless: you can just pass word to checkData().
checkData(key, ind) is incorrect: you should pass the size of the buffer for the case conversions, which is at least ind + 1 to allow for the null terminator.
the cast in putchar((char)ch); is useless and confusing.
There are some small issues in the rest of the code, but none that should cause a problem.
Start by testing your tokeniser with:
$ ./a.out <badhash2.c >zooi
$ diff badhash2.c zooi
$
Does it work for binary files, too?:
$ ./a.out <./a.out > zooibin
$ diff ./a.out zooibin
$
Yes, it does!
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
void processInput(void);
int main(int argc, char **argv) {
processInput();
return 0;
}
void processInput() {
int ch;
char *word;
int len = 0;
int cap = 60;
word = malloc(cap);
while(1) {
ch = getchar(); // (1)
if( ch != EOF && isalnum(ch)) { // (2)
if(len+1 >= cap) { // (3)
cap += cap/2;
word = realloc(word, cap);
}
word[len++] = ch;
} else {
if (len) { // (4)
#if 0
char key[len + 1];
memcpy(key, word, len); key[len] = 0;
checkData(key, len);
#else
word[len] = 0;
fputs(word, stdout);
#endif
len = 0;
}
if (ch == EOF) break; // (5)
putchar(ch);
}
}
free(word);
}
I only repaired your tokeniser, leaving out the hash table and the search & replace stuff. It is now supposed to generate a verbatim copy of the input. (which is silly, but great for testing)
If you want to allow binary input, you cannot use while((ch = getchar()) ...) : a NUL in the input would cause the loop to end. You must pospone testing for EOF, because ther could still be a final word in your buffer ...&& ch != EOF)
treat EOF just like a space here: it could be the end of a word
you must reserve space for the NUL ('\0') , too.
if (len==0) there would be no word, so no need to look it up.
we treated EOF just like a space, but we don't want to write it to the output. Time to break out of the loop.

Displaying a string one word per line using IN/OUT flag

I have written a program that first stores an arbitrary number of lines of text from the user. After that, it checks when a new word has come and if it does, then it prints it in a new line.
Below is my code:
#include<stdio.h>
#define IN 1 //inside a word
#define OUT 0 //outside a word
int main()
{
char s[100]; //storing the string entered by user
int c; //getchar reading variable
int i=0; //iterating variable
while((c=getchar())!=EOF)
{
s[i++]=c;
}
s[i]='\0'; //end of string
i=0;
int p=0; //stores the start of the word
int current=OUT; //flag to indicate if program is inside or outside a word
while(s[i]!='\0')
{
if(current==OUT) //when program is outside a word
{
if(s[i]!=' ' || s[i]!='\n' || s[i]!='\t') //word found
{
p=i; //store starting position of word
current=IN;
}
}
else if(current==IN) //program is inside a word
{
if(s[i]==' ' || s[i]=='\n' || s[i]=='\t') //end of word found
{
current=OUT; //flag now outside the word
for(int j=p;j<i;j++) //print from starting position of word
{
printf("%c",s[j]);
}
printf("\n"); //go next line
}
}
++i; //incremnent the iterator variable
}
return 0;
}
My program works well if I just enter the string in a proper manner, i.e. without any extra spaces or new lines.
But if I enter a line as follows ( notice the extra spaces and new lines):
*I am a boy
I went to Japan */
Then it prints those extra newlines and spaces along with word too, which according to me should not happen because of the IN and OUT flags.
The output is like this:
enter image description here
I request you to please help me out.
I know I can do this easily with the putchar() method checking one character at one time, but I am just curious as to what I am doing wrong in this implementation.
First bug that jumps out at me:
if(s[i]!=' ' || s[i]!='\n' || s[i]!='\t')
will always return true. You want &&, or else use a !() around the whole condition that you use the other place, for symmetry.
Or better yet, factor that out into a function, or use isspace from <ctype.h>.
Your filtering condition for determining if a character is white space is not correct. The || operator means OR. Using chained OR will allow the expression to evaluate to true every time. You need the AND operator &&. The and operator fails as soon as one operand evaluates to false, or in the case of C, 0.
Besides that, there are better ways to check for white space. One idea is using the isspace function from <ctype.h>, which accepts a character as an int, which can also be an unsigned char, and determines if that character is any of ' ', '\t', '\v', '\n' or '\r'. You can also do character checking via switch statements
switch(ch) {
case ' ':
// do something
break;
case '\n':
//do something
break;
}

explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length

This is the question on my assignment: Write a program that prompts the user to enter a sentence (assume that a sentence can have a maximum of 50 characters). It then counts the vowels and consonants in it. It also calculates the average word length of the input sentence. Word length is the total number of alphabetic characters in the sentence divided by the total number of words in it. Words are separated by one or more spaces. All the results are displayed at the end.
So far I have been able to complete all aspects of the question but I am running into a logical error on my part. When the user inputs more than a normal amount of spaces, it messes up the answer given for average word length.
Here is my code calculating average word length:
for(i = 1; sent[i] != '\0'; i++){
if( sent[i] == ' '){
++spaceCount;
}
else if((sent[i] != ' ') && (sent[i] != '\n')){
++charCount;
}
}
avgWordLength = (charCount / (spaceCount+1)) ;
Could someone help explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length
Here is a link to a previously already answered question:
Average word length for a sentence
But my school has not taught the "getchar" function yet and I would not like to use it unless I have too. To be more clear, is there away to complete the question without using the "getchar" function?
Here is an example of the problem when compiling and running
// Everything works good when
string: Thursday is ok
Average word length: 4.00 characters
// this is where my code fall apart
string: Thursday is ok
Average word length: 1.86 characters
Well, if you think about it, what you want to do is just treat any uninterrupted series of whitepace characters as one for the purpose of computing the word count. You can include ctype.h and use the isspace function to test all possible whitespace characters, or if you are supposed to do it manually, then at least check for space or tab characters (e.g. you could have a mixed sequence of spaces and tabs that should still be counted as a single (e.g. " \t \t ")
To handle multiple whitespace characters and count the sequence as one, just set a flag (e.g. ws for whitespace) and only increment spaceCount when you encounter the first whitespace, and reset the flag if another non-whitespace character is encountered.
Putting those pieces together, you could do something like the following:
int ws = 0; /* flag to treat multiple whitespace as 1 */
for(i = 0; sent[i]; i++){
if (sent[i] == ' ' || sent[i] == '\t') {
if (!ws) {
spaceCount++;
ws = 1;
}
}
else {
charCount++; /* non-whitespace character count */
ws = 0;
}
}
(note: begin your check at i = 0 to protect against Undefined Behavior in the event sent is the empty-string.)
(note2: you can check charCount before setting your first spaceCount and check ws after leaving the loop to handle leading and trailing whitespace -- and adjust spaceCount as necessary. That is left as an exercise)
Look things over and let me know if you have any further questions.
Could someone help explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length
You could use a state machine. You have two states:
1) Looking for the end of a word.
2) Looking for the end of a space sequence.
Look at the first character in the sentence. It is either the beginning of a word or a space. This tells you if you are in state 1 or 2.
If in state 1, then look for a space or the end of the sentence. If you find a space, set your state to 2.
If in state 2, then look for a non-space or the end of the sentence. If you find a non-space then set your state to 1.
counts the vowels and consonants in it. It also calculates the average word length of the input sentence.
Could someone help explain the logic behind the structure of code needed to account for extra spaces
There really is no need to count spaces. Instead all that is needed to to count the number of times a letter begins a word - it followed a non-letter - or was first character.
// pseudo code
sentence_stats(const char *s) {
vowels = 0;
consonants = 0;
word_count = 0;
previous = 0;
while (*s) {
if (isletter(*s)) { // OP to make isletter(), isvowel()
if (!isletter(previous)) {
word_count++; // start of word
}
if (isvowel(*s)) vowels++;
else consonants++;
} else if (*s == ' ') {
; // nothing to do
} else {
TBD_CODE_Handle_non_letter_non_space();
}
previous = *s;
s++;
}
average = (vowels + consonants)/word_count
}

Making a more efficient function - Word counter

I'm going through some C programming questions and I want to make sure I got the fundamentals down. Currently I'm on a word counter question:
Q: Write a function which will determine how many words are in a given string. You can assume that one or more
consecutive white spaces is a delimiter between words, and that the string you pass to your function is null terminated.
I got the thing working, but efficiency is important. I'm wondering how it can be improved. Have to use pointers and no other library besides #include(stdio.h) Thanks!
#include <stdio.h>
int word_counter(char string[])
{
//We start with first word unless we have a empty string then we have no words
int count;
if(*string!='\0'){
count=1;
}
else{
count=0;
return 0;
}
//while we dont reach the end of the string
while(*string!='\0'){
//if we detect a whitespace
if(*string==' '){
//get previous character
string--;
// If previous character is not a space we increase the count
// Otherwise we dont since we already counted a word
if(*string!=' '){
count++;
}
//return pointer to current character
string++;
}
// set pointer to next character
string++;
}
return count;
}
//just to test if it works
int main(void)
{
char str[] = "Hello World!";
printf("How many words? = %i\n", word_counter(str));
return 0;
}
Looking at your code, I see there's a special case for the initial condition of an empty string. Sometimes getting the initial condition out of the way early simplifies the rest of the algorithm, sometimes you can eliminate it by changing how you look at the problem. This time it's the second one.
If you think about this as counting the boundaries between words, the algorithm becomes simpler. There's two ways to define a word boundary, from the front, and from the back.
" Prestidigitation \n"
^ ^
front back
Are we looking for a non-whitespace character after a whitespace character? Or are we looking for a whitespace character after a non-whitespace character?
You also have code that looks backwards in the string (string--), that's often not safe because what if the string starts with whitespace? Then you've walked backwards off the string, so moving backwards should be avoided.
Finally, there's the problem of whether or not there's any whitespace at the end of the string. We'd have to special case the end of the string.
So looking at the first word boundary is the way to go: a non-whitespace character after a whitespace character. Instead of looking backwards, we'll track the state of the previous character (last_was_space below).
That's a non-whitespace character after a whitespace character. What if the string doesn't start with whitespace?
"Basset hounds got long ears."
^
What about this?
Since we have last_was_space, we can initialize it to true and pretend the start of the string starts with whitespace. This also handles leading whitespace like " this is four words".
Finally, there's more types of space than just space like tab and newline and other exotic stuff. Instead of writing if( *space == ' ' || *space == '\n' == ... ) we can use switch to make things tidy and efficient. This is one of those rare cases where you want to take advantage of its "fall through" mechanic to do the same thing for multiple cases.
#include <stdio.h>
// Note that it's `const` since we don't touch the string memory.
int word_counter(const char string[]) {
// Start with no words.
int count = 0;
// Pretend every word starts with space.
short last_was_space = 1;
// Using a for loop to make the movement of the pointer more apparent
for( ; *string!='\0'; string++ ) {
// A switch can be faster than an if/else if.
switch( *string ) {
// There's more than one type of whitespace.
// These are from isspace().
// It takes advantage of switch's fall through.
case ' ':
case '\t':
case '\n':
case '\r':
case '\v':
case '\f':
// Remember we saw a space.
last_was_space = 1;
break;
default:
if( last_was_space ) {
// Non-whitespace after space, count it
count++;
// Remember we didn't see a space.
last_was_space = 0;
}
break;
}
}
return count;
}
Normally I'd use bool from stdbool.h and isspace from ctype.h, but your exercise can only use stdio.h.

Resources