Removing consecutive repeated characters from string using C - c

I'm trying to remove consecutive repeated characters from a given string.
Example:
bssdffFdcrrrtttii ***#
output is supposed to be:
bsdfFdcrti *#
This code doesn't work and only prints the first char (b), I want to learn about my mistake.
when I'm doing a printf test, it works but not for spaces.
I think the problem might be with the new char array.
void Ex6() {
char* string[80];
scanf("%s", &string);
puts(removeDup(string));
}
char* removeDup(char *string) {
int i, c = 0;
char* newString[80];
for (i = 0; i < strlen(string); i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
return newString;
}

There are several problems with your program:
The declaration of newString should be char newString[80], i.e., an array of characters and not an array of pointers-to-characters, and likewise for the declaration in Ex6.
The call to scanf should then be scanf("%s", string), since string is already the address of an array of characters, but...
Use fgets to read a string from the user to ensure that you read whitespace, if it's important, and that the buffer is not exceeded.
newString is allocated on the stack and so should not be returned to the caller. It is better to do a char *newString = strdup(string), or, slightly less sloppy, char *newString = malloc(strlen(string)+1), which will call malloc for a block of memory sufficient to hold the original string, and thus the version without duplicates -- the comments rightly point out that this could be optimized. In principle, the caller, i.e., Ex6, must free the returned pointer to avoid a memory leak but it hardly matters in such a short program.
The result needs a null terminator: newString[c] = '\0'.
Otherwise, the removeDup function seems to work correctly.
So, putting all of that together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* removeDup(const char *string)
{
size_t i, c = 0;
size_t string_len = strlen(string);
char *newString = malloc(string_len + 1);
for (i = 0; i < string_len; i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
return newString;
}
#define MAX_STRING_LEN 80
void Ex6() {
char string[MAX_STRING_LEN];
char* result;
if (fgets(string, MAX_STRING_LEN, stdin) != NULL) {
result = removeDup(string);
printf("%s", result);
free(result);
}
}
Finally, I agree with #tadman's comment. Since the input string must anyway be traversed to calculate the length, we may as well optimize the size of the result string:
char* removeDup(const char *string)
{
size_t i, c = 0;
char *newString;
for (i = 0; string[i] != '\0'; i++)
c += (string[i] != string[i + 1]);
newString = malloc(c + 1);
for (i = c = 0; string[i] != '\0'; i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
return newString;
}

There are quite a few issues in your program. It wouldn't even compile let alone run. Also, the most problematic issue is that you are returning a pointer to a local variable from a function that ceases its scope upon completion. A simplified version of your program is as follows:
void Ex6()
{
char string[80];
scanf("%s", string);
int i, c = 0;
char newString[80];
for (i = 0; i < strlen(string); i++) {
if (string[i] != string[i + 1]) {
newString[c++] = string[i];
}
}
newString[c] = '\0';
puts(newString);
}

You can do it with O(n) time and O(1) space, by modifying existing string:
#include <stdio.h>
char* removeDup(char* input) {
char* newTail = input, *oldTail = input;
while (*oldTail) {
if (*newTail == *oldTail) {
++oldTail;
} else {
*++newTail = *oldTail++;
}
}
return newTail;
}
int main() {
char string[] = "bssdffFdcrrrtttii ***#";
char* newEnd = removeDup(string);
char* tmp = string;
while (tmp != newEnd) {
printf("%c", *tmp++);
}
//Print the last char if string had any duplicates
if(*tmp) {
printf("%c", *tmp++);
}
return 0;
}

Related

in trim function there's an if-check not working properly, why?

EDIT: I've edited my code, and this is the result:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t count_1 = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
count_1++;
}
if (count_1 < 1) {
return NULL;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1 - 1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '\0'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
}
str[count_1 - count_2] = 0;
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
now, the problem is here
for (size_t i = 0; s[i] != '\0'; i++) {
if (isspace(s[i]) == 0) { // if isspace is false.
str[i] = s[i];
}
I have a buffer overrun, even if I've checked the length. In fact, if count_1 is equal to zero, I have a buffer overrun error, but I've excluded this case, but the problem persists. By debugging line-by-line, I've noticed I have an undefined behavior.
I wanted to try to simplify the suggested solution for this exercise, therefore I've written another code for the same exercise.
this is the original answer: trim function halve the memory size to remove the whitespaces?
this is the minimal reproducible code:
#include <stdlib.h>
#include <ctype.h>
char *trim(const char *s) {
size_t count_1 = 0;
for (size_t i = 0; s[i] != '\0'; i++) {
count_1++;
}
size_t count_2 = 0;
if (isspace(s[0])) {
count_2++;
}
if (isspace(s[count_1])) {
count_2++;
}
size_t max_length = (count_1 - count_2) + 1u;
if (max_length >= count_1) {
return NULL;
}
char *str = malloc(max_length);
if (!str) {
return NULL;
}
for (size_t i = 0; s[i] != '\0'; i++) {
if (isalpha(s[i]) == 0) { // if isalpha is false.
str[i] = s[i];
}
str[count_1 - count_2] = 0;
}
return str;
}
int main(void) {
char s[] = " a b ";
char *str;
str = trim(s);
free(str);
return 0;
}
here's the detailed explanation about what I've done so far:
I've counted characters of the string s, and the length is stored in count_1.
I've counted how many whitespaces I have at the beginning of the string, and at the end of the string; and the amount is stored in count_2.
note: I've chosen to use isspace function (in <ctype.h>), because I tried to type ' ' (i.e a whitespace), but the result is not correct, and these if-checks are not evaluated whatsoever. (I used the debugger line-by-line to state this thing).
before malloc the memory I've used a check condition to avoid buffer overrun (it's similar to the question I asked yesterday), meaning I've allocated enough memory if and only if max_length is less than count_1. doing this way, I have no buffer overrun warning.
I think I can avoid to explain the final steps, because they are self explanatory and I also think they doesn't cause errors. If I'm wrong, I'll edit this point.
issue I have no clue how to fix it:
by debugging line-by-line, I've noticed that when the flow of execution goes to the 2nd if-check, the if body is not executed whatsoever. And this is strange, because the first one works fine.
There are multiple problems in your code:
count_1 is the length of the string, you should name it more explicitly as len
you return NULL if no trimming is needed. This is questionable. You should probably return a copy of the string in all cases and only return NULL in case of allocation failure.
you only test for 1 space char at the start of the string.
you only test for 1 space char at the end of the string.
furthermore this space might be counted twice if the string is " ".
max_length is a misnomer: it is not the length of the new string, but the allocation size, new_size seems more appropriate.
in the final loop, you use the same index i into the original and the new string: this is incorrect. You should use a separate index so characters from the original string can be copied after skipping the initial space.
str[count_1 - count_2] = 0; is redundant inside the loop: you should move this statement after the end of the loop.
argument values of type char should be cast as (unsigned char) when passed to the functions and macros defined in <ctype.h> to avoid undefined behavior on negative values on platforms where the char type is signed. These functions are only defined for the values of type unsigned char (between 0 and UCHAR_MAX) and the special negative value EOF. These values are the ones returned by getchar() and getc().
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *trim(const char *s) {
if (s == NULL) {
return NULL;
}
size_t start, end;
for (start = 0; isspace((unsigned char)s[start]); start++) {
continue;
}
for (end = start; s[end] != '\0'; end++) {
continue;
}
while (end > start && isspace((unsigned char)s[end - 1])) {
end--;
}
// if you are allowed to use strndup, you can return the new string this way:
//return strndup(str + start, end - start);
char *new_str = malloc(end - start + 1);
if (new_str) {
size_t j = 0; // index into the new string
for (size_t i = start; i < end; i++) {
new_str[j++] = str[i];
}
new_str[j] = '\0';
}
return new_str;
}
int main(void) {
char s[] = " a b ";
char *str = trim(s);
printf("trim(\"%s\") -> \"%s\"\n", s, str);
free(str);
return 0;
}

The program returns some weird and incomplete string

I am trying to write a function that delete whitespaces from a string but the output is not reasonable at all. I need help fam!
Code:
char* deleteSpace(char *String, int n) {
int i;
char* withoutSpaces;
withoutSpaces = calloc(n, sizeof (char));
for (i = 0; i < n - 1; i++) {
if (String[i] == ' ')
withoutSpaces[i] = String[++i];
else
withoutSpaces[i] = String[i];
}
return withoutSpaces;
}
You need to have to indices a "read" index for the source string and a "write" index for the destination.
Also, for better debugability and readability, put the index increment, ++i on a separate line. Oh - it looks like you are incrementing i twice. Once implicitly by the loop and again with the ++i.
Also unclear if n represents the length of the string with or without the null terminator. So let's just let the function deal with figuring that out via strlen.
Don't forget to null terminate the output string.
Several other bugs as well. Here's a version that's improved:
char* deleteSpace(const char *String) {
int j = 0;
const char* ptr = String;
size_t n = 0;
size_t spaces = 0;
char* withoutSpaces = NULL;
// count how many characters we expect to copy over
while (*ptr) {
n += (*ptr == ' ') ? 0 : 1;
ptr++;
}
withoutSpaces = (char*)malloc(n+1); // +1 for null char
for (i = 0; i < n; i++) {
if (String[i] != ' ') {
withoutSpaces[j] = String[i];
j++;
}
}
withoutSpaces[j] = '\0';
return withoutSpaces;
}
Also, if you just want to compact the string in place without allocating a new string.
void deleteSpace(char *String) {
char* ptrWrite = String;
while (*String) {
if (*String != ' ') {
*ptrWrite = *String;
ptrWrite++;
}
String++;
}
*ptrWrite = '\0';
}

Assign value to char in array

I'm trying to brush up on my C/C++ and I seem to have forgotten how to properly manipulate char arrays.
Code:
#include <iostream>
#include <string.h>
void reverse(char* str)
{
int numChar = strlen(str);
char *reversed = (char*)malloc(sizeof(char) * (numChar + 1));
int i = numChar;
int j = 0;
while(i >= 0)
{
reversed[j] = str[i];
j++;
i--;
printf("%c", reversed[j]);
}
printf("%s", reversed);
}
int main()
{
char* str;
strcpy(str, "apple\0");
reverse(str);
return 0;
}
I'm very certain I'm not doing what I intend to with reversed[j] = str[i] as reversed comes out empty. What's the correct way to go about this?
From first glance, In main(), memory has to be allocated to character pointer str before referencing it in strcpy
int main()
{
char* str = malloc(6) or use char str[6];
// or char *str = "apple"; is sufficient, strcpy is not required in this case
strcpy(str, "apple\0");
reverse(str);
return 0;
}
Another one :
In reverse() function, you will have to increment j after printing
while(i >= 0)
{
reversed[j] = str[i];
printf("%c", reversed[j]);
j++; //Moved here
i--;
}
reversed[j] = '\0' //Null termination
printf("\n %s", reversed);
or only the below two statements would be sufficient enough to post increment j and decrement i
reversed[j] = str[i--];
printf("%c", reversed[j++]);
Since you start with i being the strlen of the input string (e.g. HAHA -> 4), you start copying at haha[4], which is the null byte at the end of the string. IOW you just null terminated your output right at the start. Try setting i to numChar - 1.
(After fixing the problem Santosh A mentioned)
And then, make sure you null terminate the result!

Return Array of Strings with String Input

I'm trying to take a string and break it into "word" components and store that in an array of strings.
"Hello my name is Bill." should give back a char** with elements, "Hello", "my", "name", "is", and "Bill."
My code will compile however I keep encountering a runtime error (I don't get warnings anymore and my debugger gdb doesn't work)>
I'm running on minGW on Window 8.
#include <stdio.h>
#include <stdlib.h>
char** words(char* string)
{
int i = 0;
int j = 0;
int k =0;
int count = 0;
char** stringArray = (char**) malloc(sizeof(char)*30*30);
while( string[i] != '\0' )
{
if(string[i] != ' ')
{
j =0;
while(string[i+j+1] != ' ')
{
j++;
}
i = i+j;
for(k=0; k<=j; k++)
{
stringArray[count][k] = string[i+k];
}
count++;
}
i++;
}
return stringArray;
}
int main()
{
char message[20] = "abcd efgh ijkl mno";
char** wordArray = words(message);
printf("%c\n\n", wordArray[0][0]);
int i =0;
while(wordArray[i])
{
printf("%s\n", wordArray[i]);
i++;
}
printf("\nThe problem is not with the words function");
return 0;
}
There are couple of issues that have been mentioned in the comments.
The allocation should look something like:
#include <ctype.h> // for isspace()
#define MAXSTRLEN 30 // using a symbolic constant
char **stringArray;
int i, j, k;
stringArray = malloc(sizeof(char*) * MAXSTRLEN); // don't cast from malloc
for (i = 0; i < 30; ++i) {
stringArray[i] = malloc(sizeof(char) * MAXSTRLEN);
}
// TODO error checking: malloc could return NULL
while copying the substrings would look like:
i = 0;
j = 0;
while( string[i] != '\0') // go through the whole string
{
while (string[i] != '\0' && isspace(string[i])) {
i++; // skip whitespaces
}
k = 0;
while (string[i] != '\0' && !isspace(string[i])) { // copy word until whitepace or end of string
stringArray[j][k++] = string[i++];
}
stringArray[j][k] = '\0'; // EOS !!!
j++;
}
and printing (j is number of words actually read):
for (i = 0; i < j/*30*/; ++i) { // (!) how to print
printf("%s\n", stringArray[i]);
}
And, yes strtok would also do the job.
In words() you're assigning values to stringArray as a two-dimensional array, and in main() you're reading values from it as an array of pointers. Those are not the same thing.
So you need to change it so that you're consistently treating it as a 2D array, or so that you're consistently treating it as an array of pointers (char* to be exact). Either will work... see the comments above for elaboration.
This code is all wrong.
char** stringArray = (char**) malloc(sizeof(char)*30*30);
First of all, sizeof(char) is always one, second, you don't need to cast a void. So:
char **stringArray = malloc(30 * 30);
But that doesn't make any sense because it's an array of char *, so you should allocate in terms of that:
char **stringArray = malloc(sizeof(char *) * 30);
Or even better:
char **stringArray = malloc(sizeof(*stringArray) * 30);
So now you have an array with 30 char *, but each of those is not initialized, so you need to do that:
for (i = 0; i < 30; i++)
stringArray[i] = malloc(sizeof(**stringArray) * 30);
If you don't do that, you can't access stringArray[count][k].
And then you assume the last element in the array is NULL, but you never set it, so you either do stringArray[count] = NULL at the end of words(), or you do calloc() instead of malloc().
I'm not analyzing the code beyond that; it's just all wrong.

Replacing character in a string [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is the function to replace string in C?
I am trying to replace a certain character in my string with multiple characters. Here is an example of what I am trying to do.
Say I have the string "aaabaa"
I want to replace all occurrences of the character "b" with 5 "c"s.
So when I am done, "aaabaa" becomes "aaacccccaa"
I have written the following code:
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[20] = "aaabaa";
int i, j;
for (i=0; s[i]!= '\0'; i++)
{
if (s[i] == 'b')
{
for (j=0; j<5; j++)
{
s[i+j] = 'c';
}
}
}
printf("%s\n", s);
}
My output from this function is "aaaccccc". It appears that it just overwrites the last two a's with the c's. Is there any way I would have it so that these last couple of a's dont get overwritten?
If you want to do this in general, without worrying about trying to size your buffers, you should malloc a new string just large enough to hold the result:
/* return a new string with every instance of ch replaced by repl */
char *replace(const char *s, char ch, const char *repl) {
int count = 0;
const char *t;
for(t=s; *t; t++)
count += (*t == ch);
size_t rlen = strlen(repl);
char *res = malloc(strlen(s) + (rlen-1)*count + 1);
char *ptr = res;
for(t=s; *t; t++) {
if(*t == ch) {
memcpy(ptr, repl, rlen);
ptr += rlen;
} else {
*ptr++ = *t;
}
}
*ptr = 0;
return res;
}
Usage:
int main() {
char *s = replace("aaabaa", 'b', "ccccc");
printf("%s\n", s);
free(s);
return 0;
}
Your problem is that you replace the "ccccc" into the original string thus overwriting the remaining characters after what you wish to replace... You should copy into a new string and keep track of two indices - one in each.
And be happy that you declared char s[20] larger than the size of your original string plus the replace values, as otherwise you'd have created a buffer overflow vulnerability in your critical login system :-)
Cheers,
It is necessary to declare a second char array. In below code it just copies content of array s to s1 when condition fails.
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[20] = "aaabaa";
char s1[1024];
int i, j, n;
for (i=0, n = 0; s[i]!= '\0'; i++)
{
if (s[i] == 'b')
{
for (j=0; j<5; j++)
{
s1[n] = 'c';
n++;
}
}
else
{
s1[n] = s[i];
n++;
}
}
s1[n] = '\0';
printf("%s\n", s1);
}
You can use a different variable
#include <stdio.h>
#include <string.h>
int main(void)
{
char s[20] = "aaabaa";
char temp[20]="";
int i, j,k;
k=0;
for (i=0; s[i]!= '\0'; i++)
{
if (s[i] == 'b')
{
for (j=0; j<5; j++)
{
temp[k] = 'c';
k++;
}
}
else
{
temp[k]=s[i];
k++
}
}
printf("%s\n", temp);
}
#include <stdio.h>
#include <string.h>
int main(void)
{
char temp[20];
char s[20] = "aaabaa";
int i, j;
for (i=0; s[i]!= '\0'; i++)
{
if (s[i] == 'b')
{
strcpy(temp,s[i+1]); //copy rest of the string in this case 'aa'
for (j=0; j<5; j++)
{
s[i+j] = 'c';
}
s[i+j] = '\0'; // here we get s = "aaaccccc"
strcat(s,temp); // concat rest of the string (temp = "aa") after job is done.
// to this point s becomes s = "aaacccccaa"
}
}
printf("%s\n", s); //s = "aaacccccaa".
}
here we are using a buffer (temp) to store the rest of the string after our to be replaced character.
after the replacement is done we append it to the end.
so we get s = "aaacccccaa"
Well, if you're going to dynamically allocate the array, you will probably have to allocate a second array. This is necessary because your string s only has a fixed amount of memory allocated.
So, instead of tryig to overwrite the characters in your for loop, I would suggest incrementing a counter that told you how big your new array has to be. Your counter should start off as the size of your original string and increment by 4 each time an instance of 'b' is found. You should then be able to write a function that appropriately copies the modified string over to a new char buffer of size[counter], inserting 5 c's every time a 'b' is being found.
Use this function :
char *replace(char *st, char *orig, char *repl) {
static char buffer[4096];
char *ch;
if (!(ch = strstr(st, orig)))
return st;
strncpy(buffer, st, ch-st);
buffer[ch-st] = 0;
sprintf(buffer+(ch-st), "%s%s", repl, ch+strlen(orig));
return buffer;
}
for your case : printf("%s\n", replace(s,"b","ccccc"));

Resources