Changing the order of some words in a string

1.7k views Asked by At

I've been trying to solve this exercise for a week now and my code doesn't work and I can't understand why and how to change it. The exercise is: recieving a length from the user, then recieving a string (str) as long as 'length' and then recieving a number (int n) from the user.Then I need to execute the function 'void ReverseNumWords (char*str, int n).The function reverses the first n words in the string. For example: for 'I am your father StarWars' and n=3: 'your am I father StarWars'. It'll be right to assume that the words are separated by ' '. Thanks for the help!!

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

void Reverse()
{

   int len,num;
   char *str;
   printf("Please enter how many chars to allocate:\n");
   //Asking from the user the length of a string.
   scanf("%d", &len);
   //Allocating memory for the string.
   str = (char*)calloc(len, sizeof(int));
   //Making sure the allocation was successful.
   if (!str)
       printf("Error: Cannot allocate Memory\n");
   printf("Allocated %d chars\n", len);
   printf("Please enter your string:\n");
   scanf("%s", str);
   printf("Please enter how many words to reverse:\n");
   scanf("%d", &num);
   ReverseNumWords(*str, num, len);
   free(str);
}

void ReverseNumWords(char*str, int num,int len)
{

   char *sub;
   char temp;
   //Allocating memory for the string.
   sub = (char*)calloc(len, sizeof(int));
   //Making sure the allocation was successful.
   if (!sub)
       printf("Error: Cannot allocate Memory\n");
   int i, j,l;
   i = j = 0;
   for (; i < len, j <= num; i++)
       if (str[i] == '\0' || str[i] == 0)
           j++;

   for (l = 0; l < i; l++)
       sub[i] = str[i];

   for (j = 0; j < i; j++)
       temp = sub[j];
       sub[j] = sub[i - (1+j)];
       sub[i - (1+j)] = sub[j];

   reverseWords(*sub);


}

void reverseWords(char *sub)
{

   char *word_begin = sub;
   char *temp = sub;

   while (*temp)
   {
      temp++;
      if (*temp == '\0')
      {
         reverse(word_begin, temp - 1);
      }
      else if (*temp == ' ')
      {
         reverse(word_begin, temp - 1);
         word_begin = temp + 1;
      }
   } 

   reverse(sub, temp - 1);
}

void reverse(char *begin, char*sub, char *end)
{

    char temp;
    while (begin < end)
    {
        temp = *begin;
        *begin++ = *end;
        *end-- = temp;
    }
    printf("%s\n", sub);
}
2

There are 2 answers

2
RoadRunner On BEST ANSWER

In this line:

str = (char*)calloc(len, sizeof(int));

This needs to be:

str = (char*)calloc(len, sizeof(char));

I also recommend maybe using malloc to allocate memory for your strings, as you seem to be having trouble using calloc().

You would use it like this:

char *str = malloc(len+1); /* Since sizeof(char) is 1, you don't need to include it */

This is also brings up the fact that you Don't need to cast the result of malloc().

It is good to see that your checking the return value of your allocations, that 's always a good thing.

Another way to read strings:

Instead of using scanf() to read strings from stdin, you can use fgets instead.

char *fgets(char *str, int n, FILE *stream) reads a line from an input stream, and copies the bytes over to char *str, which must be given a size of n bytes as a threshold of space it can occupy.

Things to note about fgets:

  • Appends \n character at the end of buffer. Can be removed easily if you wish.
  • On error, returns NULL. If no characters are read, still returns NULL at the end.
  • Buffer must be given size n.
  • Reads specified stream. Either from stdin or FILE *.

Here is an example of how it can be used to read a line of input from stdin:

char buffer[100]; /* statically declared buffer */

printf("Enter a string: ");
fgets(buffer, 100, stdin); /* read line of input into buffer. Needs error checking */

You can also use functions like strtok and strcat to reverse and concatenate your strings.

Here is an example program which n words in a string:

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

char *allocate_chars(int *len);
void number_of_words(int *num);
void read_strings(char *str, int len);
char *reversed_words(char *str, int len, int num);
char *reverse_word(char *word);

int main(void) {
    int len, num;
    char *str, *result;

    str = allocate_chars(&len);

    read_strings(str, len);

    number_of_words(&num);

    result = reversed_words(str, len, num);
    printf("Reversed string = %s\n", result);

    free(result);
    free(str);

    return 0;
}

char *allocate_chars(int *len) {
    char *str;

    printf("Enter how many chars to allocate: ");
    if (scanf("%d", len) != 1 || *len < 1) {
        printf("Invalid length.\n");
        exit(EXIT_FAILURE);
    }
    getchar();

    str = malloc(*len+1);
    if (!str) {
        printf("Cannot allocate %d bytes for string.\n", *len+1);
        exit(EXIT_FAILURE);
    }

    printf("Allocated %d chars.\n", *len+1);

    return str;
}

void number_of_words(int *num) {
    printf("Please enter how many words to reverse: ");
    if (scanf("%d", num) != 1 || *num < 0) {
        printf("Invalid number.\n");
        exit(EXIT_FAILURE);
    }
}

void read_strings(char *str, int len) {
    int slen;

    printf("Please enter your string: ");
    if (fgets(str, len, stdin) == NULL) {
        printf("Cannot create buffer.\n");
        exit(EXIT_FAILURE);
    }

    slen = strlen(str);
    if (slen > 0) {
        if (str[slen-1] == '\n') {
            str[slen-1] = '\0';
        } else {
            printf("Entered string bigger than buffer size of %d bytes.\n", len);
            exit(EXIT_FAILURE);
        }
    }

    if (!*str) {
        printf("No string entered.\n");
        exit(EXIT_FAILURE);
    }
}

char *reversed_words(char *str, int len, int num) {
    char *reversed, *word, *reversed_word;
    const char *delim = " ";
    int count = 1;

    reversed = malloc(len+1);
    if (!reversed) {
        printf("Cannot allocate %d bytes for string.\n", len+1);
        exit(EXIT_FAILURE);
    }

    *reversed = '\0';

    word = strtok(str, delim);
    while (word != NULL) {
        if (count <= num) {
            reversed_word = reverse_word(word);
            strcat(reversed, reversed_word);
            free(reversed_word);
            count++;
        } else {
            strcat(reversed, word);
        }
        word = strtok(NULL, delim);
        if (word != NULL) {
            strcat(reversed, delim);
        }
    }

    return reversed;
}

char *reverse_word(char *word) {
    char *reverse;
    int slen, str_count = 0, i;

    slen = strlen(word);

    reverse = malloc(slen+1);
    if (!reverse) {
        printf("Cannot allocate %d bytes for string.\n", slen+1);
        exit(EXIT_FAILURE);
    }

    for (i = slen-1; i >= 0; i--) {
        reverse[str_count++] = word[i];
    }
    reverse[str_count] = '\0';

    return reverse;
}

Sample Input:

Enter how many chars to allocate: 50
Allocated 51 chars.
Please enter your string: Hello what a lovely day
Please enter how many words to reverse: 4

Output:

Reversed string = olleH tahw a ylevol day
5
Gabriel Pellegrino On

Your read function, should be something like this

int len,num;
   char *str;
   printf("Please enter how many chars to allocate:\n");
   //Asking from the user the length of a string.
   scanf(" %d", &len);
   //Allocating memory for the string.
   str = (char*)malloc(sizeof(char)*(len+1));
   //Making sure the allocation was successful.
   if (!str)
       printf("Error: Cannot allocate Memory\n");

   printf("Allocated %d chars\n", len);
   printf("Please enter your string:\n");
   scanf(" %[^\n]s", str);

   printf("Please enter how many words to reverse:\n");
   scanf(" %d", &num);
   ReverseNumWords(*str, num, len);
   free(str);

Because when reading with %s, you'll stop at the first ' ' (blank space) you find. And you want to read until you find a \n (enter).