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;
butchar *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.