简体   繁体   中英

C : removing duplicated letters from string

I am trying to remove duplicated letters in each word from string.(I haven't specified it for upper and lower case letters yet)

Input:

Ii feel good    todday!!
thhis iss   fixed

Output:

I fel god today!
this is fixed

I am calling this function in the main and i have to use the result in another function. That's why I call it by reference.

int main(){
char string[100];
printf("Enter a string:");
gets(string);
dup_letters_rule(&string);
return 0;
}

void dup_letters_rule(char *str_[]){
char new_str_[100];
int i=0, j=0;
printf("Fixed duplicates:\n");
while(*str_[i]!='\0'){
    if(*str_[i]== *str_[i+1] && *str_[i+1]!='\0'){
        while(*str_[i]==*str_[i+1] && *str_[i+1]!='\0'){
            i++;
        }
        *str_[i]=new_str_[j];
        j++;
        i++;
    }
    else{
        *str_[i]=new_str_[j];
        j++;
        i++;
    }
}
new_str_[j]='\0';
puts(new_str_);
}
 

It works like:

void dup_letters_rule(char *str_[]){
char *new_str_=*str_, *temp=*str_;
temp++;
printf("Fixed duplicates:\n");
while(*new_str_!='\0'){
    if(*new_str_== *temp && *temp!='\0'){
        while(*new_str_==*temp && *temp!='\0'){
            new_str_++;
            temp++;
        }
        putchar(*new_str_);
        new_str_++;
        temp++;
    }
    else{
         putchar(*new_str_);
         new_str_++;
         temp++;
    }
}
}

But then, I can't use *str_ string in another function.

The code can be simplified.

We can keep an int value that is the previous char seen and compare it against the current char and only "copy it out" if they are different. (ie we only need two pointers).

We also have to use tolower because Ii goes to I .

Although a second/output buffer could be used, the function can do the cleanup "in-place". Then, the caller can use the cleaned up buffer. This is what we'd normally want to do.

If the caller needs to keep the original string, it can save the original to a temp buffer and call the function with the temp

I had to refactor your code. I tested it against your sample input. It is annotated:

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

void
dup_letters_rule(char *src)
{
    char *dst = src;
    int prev = -1;

    // rchr -- the "raw" char
    // lchr -- the result of tolower(rchr)
    // prev -- the previous value of lchr (starts with -1 to force output of
    //         first char)
    for (int rchr = *src++;  rchr != 0;  rchr = *src++) {
        // get lowercase char
        int lchr = tolower((unsigned char) rchr);

        // output if _not_ a dup
        if (lchr != prev)
            *dst++ = rchr;

        // remember this char for the next iteration
        prev = lchr;
    }

    *dst = 0;
}

int
main(void)
{
    char *cp;
    char buf[1000];

    while (1) {
        cp = fgets(buf,sizeof(buf),stdin);
        if (cp == NULL)
            break;

        // get rid of newline
        buf[strcspn(buf,"\n")] = 0;

        // eliminate dups
        dup_letters_rule(buf);

        // output the clean string
        printf("%s\n",buf);
    }

    return 0;
}

UPDATE:

can i print the clean string in the dup_letters_rule function? – hamster

Sure, of course. We're the programmers, so we can do whatever we want;-)

There is a maxim for functions: Do one thing well

In many actual (re)use cases, we don't want the simple/low level function to do printing. That is the usual.

But, we could certainly add printing to the function. We'd move the printf from main into the function itself.

To get the best of both worlds, we can use two functions. One that just does the transformation. And, a second that calls the simple function and then prints the result.

Here's a slight change that illustrates that. I renamed my function and created dup_letters_rule with the printf embedded in it:

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

void
dup_letters_rule_basic(char *src)
{
    char *dst = src;
    int prev = -1;

    // rchr -- the "raw" char
    // lchr -- the result of tolower(rchr)
    // prev -- the previous value of lchr (starts with -1 to force output of
    //         first char)
    for (int rchr = *src++;  rchr != 0;  rchr = *src++) {
        // get lowercase char
        int lchr = tolower((unsigned char) rchr);

        // output if _not_ a dup
        if (lchr != prev)
            *dst++ = rchr;

        // remember this char for the next iteration
        prev = lchr;
    }

    *dst = 0;
}

void
dup_letters_rule(char *buf)
{

    dup_letters_rule_basic(buf);

    // output the clean string
    printf("%s\n",buf);
}

int
main(void)
{
    char *cp;
    char buf[1000];

    while (1) {
        cp = fgets(buf,sizeof(buf),stdin);
        if (cp == NULL)
            break;

        // get rid of newline
        buf[strcspn(buf,"\n")] = 0;

        dup_letters_rule(buf);
    }

    return 0;
}

UPDATE #2:

and why it's not char *dst = *src; but char *dst = src; – hamster

This is basic C. We want dst to have the same value/contents that src does. Just as if we did:

int x = 23;
int y = x;

If we do what you're suggesting, the compiler flags the statement:

bad.c: In function ‘dup_letters_rule_basic’:
bad.c:8:14: warning: initialization of ‘char *’ from ‘char’ makes pointer from integer without a cast [-Wint-conversion]
  char *dst = *src;
              ^

Doing char *dst = *src [as you suggest] is using * in two different ways.

Doing char *dst says that dst is defined as a pointer to a char .

Doing *src here [which is the initializer for dst and is an expression ], the * is the dereference operator . It says "fetch the value (a char ) pointed to by src ". Not what we want.

Perhaps this would be more clear if we didn't use an initializer. We use a definition ( without an initializer) and set the initial value of dst with an assignment statement:

char *dst;  // define a char pointer (has _no_ initial value)
dst = src;  // assign the value of dst from the value of src

The assignment [statement] can occur anywhere after the definition and before the for loop/statement. Here's the first few lines of the function body:

char *dst;
int prev = -1;

dst = src;

To remove the duplicate consecutive characters from a string in-place, keep track of position in string where the next character, which is not same as its previous character, to be write and check current processing character with previous character (ignore the difference in their case) except when the character is the first character of string because the first character does not have any character previous to it. If current processing character is same as previous character then move to next character in the string and if they are not same then overwrite the character at tracked position with current processing character and increment tracked position pointer by 1.

Its implementation:

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

void remove_consecutive_dup_chars (char * pstr) {
    if (pstr == NULL) {
        printf ("Invalid input..\n");
        return;
    }

    /* Pointer to keep track of position where next character
     * to be write in order to remove consecutive duplicate character.
     */
    char * p = pstr;
    for (unsigned int i = 0; pstr[i] ; ++i) {
        if ((i) && (tolower (pstr[i]) == tolower (pstr[i - 1]))) {
            continue;
        }

        *p++ = pstr[i];
    }

    /* Add the null terminating character. 
     */
    *p = '\0';
}

int main (void) {
    char buf[256] = {'\0'};

    strcpy (buf, "Ii feel good    todday!!");
    remove_consecutive_dup_chars (buf);
    printf ("%s\n", buf);

    strcpy (buf, "thhis iss   fixed");
    remove_consecutive_dup_chars (buf);
    printf ("%s\n", buf);

    strcpy (buf, "");
    remove_consecutive_dup_chars (buf);
    printf ("%s\n", buf);

    strcpy (buf, "aaaaaa    zzzzzz");
    remove_consecutive_dup_chars (buf);
    printf ("%s\n", buf);
    
    return 0;
}

Output:

I fel god today!
this is fixed

a z

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