简体   繁体   中英

reverse vowels in string in c

I'm a beginner programmer. I was trying out the problem of reversing vowels in a string.

Ex: input: zabedfigu, output: zubidfega

When I run the following code, I get a runtime error. I've tried to change the conditions within incrementing the pointer pc1 upto only upto the middle index etc., but that either gives me a runtime error or doesn't give me the required output. I'd like some help on what to do to make my code work as well as any new way of solving the problem. TIA.

#include<stdio.h>

char* reverseVowels(char* str)
{
    char *pc1, *pc2;
    int i;
    pc1 = &str[0];
    for(i=0; str[i]!='\0';++i)
        ;
    pc2 = &str[i-1];

    while(pc1!=pc2)
    {
      if((*pc1=='a')||(*pc1=='e')||(*pc1=='i')||(*pc1=='o')||(*pc1=='u'))
      {
        while(pc2!=pc1)
        {
            if((*pc2=='a')||(*pc2=='e')||(*pc2=='i')||(*pc2=='o')||(*pc2=='u'))
            {
              char temp;
              temp = *pc1;
              *pc1 = *pc2;
              *pc2 = temp;
              ++pc2;
              break;
            }
            else
              ++pc2;
        }
        ++pc1;
      }
      else
        ++pc1;
    }
    //return str;
    return NULL;
}
int main()
{
    char string[20], *pstr;
    scanf("%s", string);
    //pstr = reverseVowels(string);
    //printf("%s", pstr);
    reverseVowels(string);
    printf("%s", string);
    return 0;
}

The problem here is that you are incrementing both pointers the one in the 0 position, and the one in the end position. The first pointer should increment, and the second one should decrement, thus instead of doing this:

++pc2;

You should do this

--pc2;

The problem is occurring when you are going to increment the pointer variable value of pc2 , instead of decrementing the pointer variable pc2 value like this --pc .

Updated

According to cleblanc 's comment , my previous answer was not working for an input like abade , so then I changed the code to fix that problem.

#include <stdio.h>

char* reverseVowels(char* str)
{
    char *pc1, *pc2;
    int i;
    pc1 = &str[0];
    for(i=0; str[i]!='\0';++i)
        ;
    pc2 = &str[i-1];

    while(pc1<pc2)
    {
      if((*pc1=='a')||(*pc1=='e')||(*pc1=='i')||(*pc1=='o')||(*pc1=='u'))
      {
        while(pc2!=pc1)
        {
            if((*pc2=='a')||(*pc2=='e')||(*pc2=='i')||(*pc2=='o')||(*pc2=='u'))
            {
              char temp;
              temp = *pc1;
              *pc1 = *pc2;
              *pc2 = temp;
              --pc2;
              break;
            }
            else
              --pc2;
        }
        ++pc1;
      }
      else
        ++pc1;
    }
    //return str;
    return NULL;
}
int main()
{
    char string[20], *pstr;
    scanf("%s", string);
    //pstr = reverseVowels(string);
    //printf("%s", pstr);
    reverseVowels(string);
    printf("%s\n", string);
    return 0;
}

You have several answers and comments pointing out the fundamental flaw in your code — that you're incrementing pc2 instead of decrementing it. However, I think your algorithm is more complicated than need be. You could:

  • Subject at all times to pc1 < pc2 :
    • If pc1 is not pointing at a vowel, increment it
    • If pc2 is not pointing at a vowel, decrement it
    • If the pointers are different, swap the vowels and adjust the pointers

With test code, and with the addition of an is_vowel() function which detects both upper-case and lower-case vowels, I ended up with:

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

static inline int is_vowel(int c)
{
    c = tolower(c);
    return (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u');
}

static void reverse_vowels(char *string)
{
    char *p1 = string;
    char *p2 = p1 + strlen(p1);     // '\0' is not a vowel

    while (p1 < p2)
    {
        while (p1 < p2 && !is_vowel((unsigned char)*p1))
            p1++;
        while (p1 < p2 && !is_vowel((unsigned char)*p2))
            p2--;
        if (p1 != p2)
        {
            char c = *p1;
            *p1++ = *p2;
            *p2-- = c;
        }
    }
}

int main(void)
{
#ifdef INTERACTIVE
    char line[1024];
    while (fgets(line, sizeof(line), stdin) != NULL)
    {
        line[strcspn(line, "\n")] = '\0';
        printf("Input:  [%s]\n", line);
        reverse_vowels(line);
        printf("Output: [%s]\n", line);
    }
#else
    char strings[][40] =
    {
        "",
        "a",
        "b",
        "ab",
        "abe",
        "abeci",
        "nnnnummmmmmmmmmmmippppoqq",
        "AbleWasIEreISawElba",
        "A Man, A Plan, A Canal - Panama!"
    };
    enum { NUM_STRINGS = sizeof(strings) / sizeof(strings[0]) };

    for (int i = 0; i < NUM_STRINGS; i++)
    {
        printf("Input:  [%s]\n", strings[i]);
        reverse_vowels(strings[i]);
        printf("Output: [%s]\n", strings[i]);
    }
#endif /* INTERACTIVE */
    return 0;
}

You can compile it with -DINTERACTIVE to give you an interactive test, or by default it gives a fixed set of tests.

Default output:

Input:  []
Output: []
Input:  [a]
Output: [a]
Input:  [b]
Output: [b]
Input:  [ab]
Output: [ab]
Input:  [abe]
Output: [eba]
Input:  [abeci]
Output: [ibeca]
Input:  [nnnnummmmmmmmmmmmippppoqq]
Output: [nnnnommmmmmmmmmmmippppuqq]
Input:  [AbleWasIEreISawElba]
Output: [ablEWasIerEISawelbA]
Input:  [A Man, A Plan, A Canal - Panama!]
Output: [a Man, a Plan, a CAnal - PAnamA!]

Sample interactive session (my program was called rv61 ):

$ rv61

Input:  []
Output: []
a
Input:  [a]
Output: [a]
b
Input:  [b]
Output: [b]
ab
Input:  [ab]
Output: [ab]
ae
Input:  [ae]
Output: [ea]
abcde
Input:  [abcde]
Output: [ebcda]
ablewasiereisawelba
Input:  [ablewasiereisawelba]
Output: [ablewasiereisawelba]
palindromic nonsense 
Input:  [palindromic nonsense]
Output: [pelendromic nonsinsa]
vwlsmssng
Input:  [vwlsmssng]
Output: [vwlsmssng]
AManAPlanACanal-Panama!
Input:  [AManAPlanACanal-Panama!]
Output: [aManaPlanaCAnal-PAnamA!]
  a big and complex sentence with multiple words of a number of lengths and so on  
Input:  [  a big and complex sentence with multiple words of a number of lengths and so on  ]
Output: [  o bog and cemplox sentunca woth moltepli wurds if e nember ef longths and si an  ]
$

Note that the testing tests a number of degenerate cases — an empty string, a string with no vowels, a string with one vowel, etc. The palindromic tests benefit from supporting mixed case — it's hard to spot that vowels have been swapped if they're all lower case and the text is a palindrome.

Another test that could be applied is to reverse the vowels twice; the output should be the same as the input. Conservation tests can be important. (If you had a sort which didn't preserve all the elements in the array but added random new ones and/or dropped initial ones, you wouldn't be happy. But that's a topic for another day.)

Having a simple test harness along the lines shown can be helpful for library functions. Many of my library functions have a #ifdef TEST#endif at the end to allow them to be tested for sanity. The best tests verify that the result is what is expected; these ones are lazy and leave it to visual inspection to validate the output. If it was a library function, there'd be a header to declare the function which would be #include d in the source, and the function would not be static . (My default compilation options require either a declaration of the function before it is defined, or the function must be static . I make functions static in sample code like this since there's no other file referencing the function, so there's no need for a header to declare the function, and only headers should declare externally visible functions.)

Note too that the is_vowel name is carefully chosen to avoid the reserved names in the C standard:

Function names that begin with either is or to , and a lowercase letter may be added to the declarations in the <ctype.h> header.

Using isVowel() would have been OK too; using isvowel() would be using a reserved name.


Bill Woodger commented :

Why the comment about '\\0' not being a vowel?

With the code as shown, after the initialization of p2 , it is true that *p2 == '\\0' . The observation the '\\0' is not a vowel matters if the string is non-empty because if it matched the is_vowel() predicate, the null byte could be moved to some point earlier in the string, truncating it.

Suppose the function was reverse_controls() instead of reverse_vowels() and the test used iscntrl() instead of is_vowel() . Then the code would have to handle it differently for a non-zero length string because the null byte would be reported as a control character and that would send things awry — it would be swapped with the first other control character (if there was another) in the string, truncating the string. That is not what's intended.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM