1

Function has to eliminate all extra spaces between the words and punctuation marks but punctuation marks must not have any space between them and the word before them.

for example i have this string:

Hey   ,how   are you today    ?

and i should get this:

Hey, how are you today?

this function eliminates extra spaces in here. But I don't know how to include the punctuation marks

btw I am caling this function in the main function

void space_rule(char *str[]){
printf("Enter a string: ");
gets(*str);
puts(*str);

char *p = strtok(*str, " ");
    while(*p!=NULL){
    printf("%s ",p);
    p=strtok(NULL, " ");
}

}
  • Your example result shows that you also want to **add** space after `,`. Is that right? – Barmar May 13 '21 at 15:12
  • 5
    Stop using `gets()` immediately! https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used – Barmar May 13 '21 at 15:13
  • What is the "first part"? That is what is the result from your code - show that too to be clear. Your solution is over complicated BTW. What are you passing in `str` and an argument? – Clifford May 13 '21 at 15:19
  • @Barmar yes right. A space needed between the punctuation and the beginning of the next word. –  May 13 '21 at 15:20
  • Iterate through the string and set a flag true if you retrieve a space. If the flag is true, skip the current character, else write it to the current write (!) position of the string. If you get an other character than space, set the flag to false. So you have to handle two indices (one write and one read), don't forget to set the `\0` at the end of the string. – Jozott May 13 '21 at 15:21
  • You can't do this with `strtok()`. You'll need to process it character by character, and keep track of the state. – Barmar May 13 '21 at 15:21
  • @Jozott so it'll work for both letters and punctuation marks right? I'll try –  May 13 '21 at 15:28
  • @Barmar should I use any string.h function? –  May 13 '21 at 15:30
  • Maybe `strcspn()` could be useful for this, but I'm not sure. – Barmar May 13 '21 at 15:33
  • Yes sure, you can look for them and act differently when you retrieve one. – Jozott May 13 '21 at 15:34
  • Your function `space_rule()` should take not only the buffer but also his length. And since characters may be added, you need an output buffer with his max length. (Think about "abc,def,ghi" input string. The output would be longer. OK, it can be managed with passing an input buffer longer than the input string. – fpiette May 13 '21 at 15:37
  • Modify the question to clarify the rules - do not hide them in the comments. In particular the _space after punctuation_ rule. Are you sure all punctuation should be treated the same? How about quotes? How about punctuation at the end - such as `'?' or `'.'`` for example? – Clifford May 13 '21 at 16:05
  • I suggest that you get the input in the calling function and have `void space_rule( const char* str )` which only processes the string. It is bad practice to have the input and the processing in the same function - it make the function very specific and not reusable. Also `strtok()` modifies the string - that is often not what you want, and it means you cannot use a `const`. – Clifford May 13 '21 at 16:16
  • Why in any case are you passing a `char** str` only to dereference it everywhere? – Clifford May 13 '21 at 16:22
  • @Clifford I am calling the function by reference. Shouldn't I use '*str' everywhere? –  May 13 '21 at 16:42
  • It is a string, it is passed by reference already. If you simply passed `char* str` (or `char str[]` if you must - I wouldn't), you would not need the dereference. `char str[]` suggests that you can pass an array by copy, when you cannot, it still has type `char*` even if you passed an array as the argument, and if you didn't pass an array, the array notation is misleading. You only need to pass a `char**` in this situation if you were going to modify the caller's pointer to reference an entirely different string. – Clifford May 13 '21 at 17:20
  • You are right you need to dereference `str` everywhere because you passed it by reference, but passing it by reference serves no purpose. The point is you are passing a reference-to-a-reference and then dereferencing it to access the reference. – Clifford May 13 '21 at 17:24

2 Answers2

1

Consider:

#include <stdio.h>
#include <ctype.h>

int main()
{
    const char* input = "Hey   ,how   are you today    ?" ;
    const char* chp = input ;
    
    // While not end of input...
    while( *chp != '\0' )
    {
        
        // Print all regular characters (not space or punctuation)
        if( !(isspace( *chp ) || ispunct( *chp )) )
        {
            putchar( *chp ) ;
            chp++ ;
        }
        // Print all punctuation followed by a space (except at end)
        else if( ispunct( *chp ) )
        {
            putchar( *chp ) ;
            chp++ ;
            if( *chp != '\0' && !isspace( *chp ) )
            {
                putchar( ' ' ) ;
            }
        }
        // Is space...
        else if( isspace( *chp ) )
        {
            // Skip all space
            while( *chp != '\0' && isspace( *chp ) )
            {
                chp++ ;
            }
            
            // If not end, and not punctuation...
            if( *chp != '\0' && !ispunct( *chp ) )
            {
                // ...insert single space 
                putchar( ' ' ) ;
            }
        }
    }
    
    return 0;
}

However it is likely that you need to refine your rules more carefully. Perhaps not all punctuation should be treated the same? For example for :

const char* input = "She said \"Hello\"" ;

The output is:

She said" Hello"  

which is unlikely to be intended. For that not only would you need to include an exception rule for '"', you'd need to account for opening and closing quotes and apply the rule accordingly. An exercise for the reader - I suggest you post a new question if you remain stuck with that.

It gets really complicated if you have a string such as :

She said, "Hey, how are, you today?"

Because then you have ? and " together, for ? you want a rule: "space after punctuation unless next character is also punctuation". But if the ? and " are separated by space you have to eliminate that first before making the decision. To be honest, I gave up trying to figure out all the permutations. I would suggest that if you want to do that you perform the transform in multiple passes applying one rule at a time, for example:

  1. Reduce all multiple spaces to 1 space
  2. Insert space after punctuation if no space is present, and it is not an opening quote ( ' or " ).
  3. Remove space between punctuation
  4. Insert space before opening quote if no space is present.

By having simpler rules (executed in separate functions) and multiple passes, it will be much easier to get right at the expense of efficiency. You can also then easily re-order the application of the rules to get the intended result. For example here rule 4 must be applied after rule 3.

Clifford
  • 88,407
  • 13
  • 85
  • 165
0

The changes you want can all be expressed as changes based on the types of each pair of adjacent characters. You need to characterize each character as a space, a letter, or punctuation, and deal with pairs as follows:

  • <space><space>: remove one (the first?) space
  • <space><punct>: remove the space
  • <punct><letter>: insert a space between them
  • otherwise: leave it alone.

Since these pair rules never do anything to the second character in the pair, this suggests a simple loop copying characters from an input buffer to an output buffer, doing those changes as you go:

void copy_and_fix(char *out, const char *in) {
    while (*in) {
        if (isspace(in[0]) && isspace(in[1])) {
            // don't copy the first space
        } else if (isspace(in[0]) && ispunct(in[1])) {
            // don't copy the space
        } else if (ispunct(in[0]) && iswordchar(in[1])) {
            // keep the punct and insert a space;
            *out++ = *in;
            *out++ = ' ';
        } else {
            // just copy the character
            *out++ = *in;
        }
        ++in;
    }
    *out = '\0';  // NUL terminate the output.
}

Of course, you need to be careful about the sizes of your buffers, to make sure you don't overflow the output buffer, but that can be dealt with a variety of ways (ensure the output buffer is at least 1.5 the size of the input, or pass a size and truncate the output if necessary, or allocate the output buffer on the heap and resize as needed).

Chris Dodd
  • 119,907
  • 13
  • 134
  • 226