Split string in C every white space

C

C Problem Overview


I want to write a program in C that displays each word of a whole sentence (taken as input) at a seperate line. This is what i have done so far:


void manipulate(char *buffer);
int get_words(char *buffer);

int main(){
    char buff[100];

    printf("sizeof %d\nstrlen %d\n", sizeof(buff), strlen(buff));   // Debugging reasons

    bzero(buff, sizeof(buff));

    printf("Give me the text:\n");
    fgets(buff, sizeof(buff), stdin);

    manipulate(buff);
    return 0;
}

int get_words(char *buffer){                                        // Function that gets the word count, by counting the spaces.
    int count;
    int wordcount = 0;
    char ch;

    for (count = 0; count < strlen(buffer); count ++){
        ch = buffer[count];
        if((isblank(ch)) || (buffer[count] == '\0')){                   // if the character is blank, or null byte add 1 to the wordcounter
            wordcount += 1;
        }
    }
    printf("%d\n\n", wordcount);
    return wordcount;
}

void manipulate(char *buffer){
    int words = get_words(buffer);
    char *newbuff[words];
    char *ptr;
    int count = 0;
    int count2 = 0;
    char ch = '\n';
    
    ptr = buffer;
    bzero(newbuff, sizeof(newbuff));

    for (count = 0; count < 100; count ++){
        ch = buffer[count];
        if (isblank(ch) || buffer[count] == '\0'){
            buffer[count] = '\0';
            if((newbuff[count2] = (char *)malloc(strlen(buffer))) == NULL) {
                printf("MALLOC ERROR!\n");
                exit(-1);
            }
            strcpy(newbuff[count2], ptr);
            printf("\n%s\n",newbuff[count2]);
            ptr = &buffer[count + 1];
            count2 ++;
        }
    }
}

Although the output is what i want, i have really many black spaces after the final word displayed, and the malloc() returns NULL so the MALLOC ERROR! is displayed in the end. I can understand that there is a mistake at my malloc() implementation but i do not know what it is.

Is there another more elegant - generally better way to do it?

Thanks in advance.

C Solutions


Solution 1 - C

http://www.cplusplus.com/reference/clibrary/cstring/strtok/

Take a look at this, and use whitespace characters as the delimiter. If you need more hints let me know.

From the website:

char * strtok ( char * str, const char * delimiters );

> On a first call, the function expects a C string as argument for str, whose first character is used as the starting location to scan for tokens. In subsequent calls, the function expects a null pointer and uses the position right after the end of last token as the new starting location for scanning.

> Once the terminating null character of str is found in a call to strtok, all subsequent calls to this function (with a null pointer as the first argument) return a null pointer.

> Parameters > ====

> - str > - C string to truncate. > - Notice that this string is modified by being broken into smaller strings (tokens). Alternativelly [sic], a null pointer may be specified, in which case the function continues scanning where a previous successful call to the function ended. > - delimiters > - C string containing the delimiter characters. > - These may vary from one call to another.

> Return Value > ===== > A pointer to the last token found in string. > A null pointer is returned if there are no tokens left to retrieve.

> Example > =====

/* strtok example */
#include <stdio.h>
#include <string.h>

int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
  {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
  }
  return 0;
}

Solution 2 - C

For the fun of it here's an implementation based on the callback approach:

const char* find(const char* s,
                 const char* e,
                 int (*pred)(char))
{
    while( s != e && !pred(*s) ) ++s;
    return s;
}

void split_on_ws(const char* s,
                 const char* e,
                 void (*callback)(const char*, const char*))
{
    const char* p = s;
    while( s != e ) {
        s = find(s, e, isspace);
        callback(p, s);
        p = s = find(s, e, isnotspace);
    }
}

void handle_word(const char* s, const char* e)
{
    // handle the word that starts at s and ends at e
}

int main()
{
    split_on_ws(some_str, some_str + strlen(some_str), handle_word);
}

Solution 3 - C

malloc(0) may (optionally) return NULL, depending on the implementation. Do you realize why you may be calling malloc(0)? Or more precisely, do you see where you are reading and writing beyond the size of your arrays?

Solution 4 - C

Consider using strtok_r, as others have suggested, or something like:

void printWords(const char *string) {
    // Make a local copy of the string that we can manipulate.
    char * const copy = strdup(string);
    char *space = copy;
    // Find the next space in the string, and replace it with a newline.
    while (space = strchr(space,' ')) *space = '\n';
    // There are no more spaces in the string; print out our modified copy.
    printf("%s\n", copy);
    // Free our local copy
    free(copy);
}

Solution 5 - C

Something going wrong is get_words() always returning one less than the actual word count, so eventually you attempt to:

char *newbuff[words]; /* Words is one less than the actual number,
so this is declared to be too small. */

newbuff[count2] = (char *)malloc(strlen(buffer))

count2, eventually, is always one more than the number of elements you've declared for newbuff[]. Why malloc() isn't returning a valid ptr, though, I don't know.

Solution 6 - C

You should be malloc'ing strlen(ptr), not strlen(buf). Also, your count2 should be limited to the number of words. When you get to the end of your string, you continue going over the zeros in your buffer and adding zero size strings to your array.

Solution 7 - C

Just as an idea of a different style of string manipulation in C, here's an example which does not modify the source string, and does not use malloc. To find spaces I use the libc function strpbrk.

int print_words(const char *string, FILE *f)
{
   static const char space_characters[] = " \t";
   const char *next_space;

   // Find the next space in the string
   //
   while ((next_space = strpbrk(string, space_characters)))
   {
      const char *p;

      // If there are non-space characters between what we found
      // and what we started from, print them.
      //
      if (next_space != string)
      {
         for (p=string; p<next_space; p++)
         {
            if(fputc(*p, f) == EOF)
            {
               return -1;
            }
         }

         // Print a newline
         //
         if (fputc('\n', f) == EOF)
         {
            return -1;
         }
      }

      // Advance next_space until we hit a non-space character
      //
      while (*next_space && strchr(space_characters, *next_space))
      {
         next_space++;
      }

      // Advance the string
      //
      string = next_space;
   }

   // Handle the case where there are no spaces left in the string
   //
   if (*string)
   {
      if (fprintf(f, "%s\n", string) < 0)
      {
         return -1;
      }
   }

   return 0;
}

Solution 8 - C

you can scan the char array looking for the token if you found it just print new line else print the char.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main()
    {
        char *s;
        s = malloc(1024 * sizeof(char));
        scanf("%[^\n]", s);
        s = realloc(s, strlen(s) + 1);
        int len = strlen(s);
        char delim =' ';
        for(int i = 0; i < len; i++) {
            if(s[i] == delim) {
                printf("\n");
            }
            else {
                printf("%c", s[i]);
            }
        }
        free(s);
        return 0;
    }

Solution 9 - C

char arr[50];
gets(arr);
int c=0,i,l;
l=strlen(arr);

	for(i=0;i<l;i++){
		if(arr[i]==32){
			printf("\n");
		}
		else
		printf("%c",arr[i]);
	}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionredsoljaView Question on Stackoverflow
Solution 1 - CHortinsteinView Answer on Stackoverflow
Solution 2 - CwilhelmtellView Answer on Stackoverflow
Solution 3 - CephemientView Answer on Stackoverflow
Solution 4 - CStephen CanonView Answer on Stackoverflow
Solution 5 - CDoddyView Answer on Stackoverflow
Solution 6 - CBartosz MilewskiView Answer on Stackoverflow
Solution 7 - CasveikauView Answer on Stackoverflow
Solution 8 - CFahad AlotaibiView Answer on Stackoverflow
Solution 9 - CrazAguls deztinyView Answer on Stackoverflow