How does this clean up the empty space in an array? - c

void empty_spaces(char array[]){
int j=0,i=0,n=0;
n=strlen(array);
while(i<n){
if(array[i]==' '){
j=i;
while(j<n){
array[j]=array[j+1];
++j;
}
--n;
}else
++i;
}
if(n>15)
n=15;
array[n]='\0';
}
Could someone explain me, this code? This function cleans up the empty spaces in array, but could someone explain me exactly what it works?

It's a rather flabby attempt at a function that removes spaces from a string. The problem with the code is that it has gratuitous iteration and it turns an O(n) algorithm into an O(n^2) algorithm.
Rather than trying to understand the code you have I feel it is best to do it the efficient and simple way. Like this.
void empty_spaces(char str[])
{
char *src = str;
char *dst = str;
while (*src)
{
if (*src != ' ')
{
*dst = *src;
dst++;
}
src++;
}
*dst = '\0';
}
We perform a single pass across the string with two pointers, src and dst. When a non-space character is encountered it is copied from source to destination. Maintaining two separate pointers into the array avoids the spurious iteration from your code.
I ignored the n>15 part of your code. The effect of that is that the string is always truncated to have length no greater than 15 characters, but quite why that would be done is mysterious to me. It surely shouldn't be mixed up in this function.
Since I've not really answered the question as asked, but since I hope this is useful to you, I have made the answer community wiki.

A rewritten and commented version of the above:
//....
n = strlen(array); // n is the number of characters in the array up to the final 0
while (i < n) {
if (array[i] != ' ') { // not a space
i++; // next char,
continue; // continue
}
j = i; // j is the current array index
while (j < n) { // while there are chars left...
array[j] = array[j+1]; // copy the next character into the current index
j++;
}
n--; // and remove one from the string len since a space is removed
}
The code after that limits the string length to 15 before returning.
So this code removes spaces and possibly truncates the string to 15 chars only.

It loops over every character in the array, removing all ' ' (space) characters. The inner loop is what does the erasing. When the outer loop finds a space character, the inner loop "shifts" the rest of the array to the left one index, overwriting the space.

Basically, as soon as the loop encounters ' ' character (space), it moves all the elements of array one place ' left ', therefore replacing the space with the following character.

Related

How the while statement is executed in C or how this array-referenced pointers work?

I started learning C and I had this exercise from the book "Prentice Hall - The C Programming Language".
Chapter 5 Exercise 3:
Write a pointer version of the fuction strcat that we showed in Chapter 2. strcat(s, t) copies the string t to the end of s.
I did the exercise but the first method that came up to my mind was:
void stringcat(char *s, char *t){
int i,j;
i = j = 0;
while(*(s+i) != '\0'){
printf("%d", i);
i++;
}
while ( (*(t+j)) != '\0'){
*(s+i) = *(t+j);
i++;
j++;
}
}
In main I had:
int main(){
char s[] = "Hola";
char t[] = "lala";
stringcat(s,t);
printf("%s\n", s);
}
At first sight I thought it was right but the actual output was Holalalaa.
Of course it was not the output that I expected, but then I coded this:
void stringcat(char *s, char *t){
int i,j;
i = j = 0;
while(*(s+i) != '\0'){
printf("%d", i);
i++;
}
while((*(s+i) = *(t+j)) != '\0'){
i++;
j++;
}
}
And the output was right.
But then I was thinking a lot about the first code because it's very similar to the second one but why the first output was wrong?. Is it something related with the while statement? or something with pointers?. I found it really hard to understand because you can't see what's happening in the array.
Thanks a lot.
Your code has more than the one problem that you found, but let's start with it.
Actually you are asking why
/* ... */
while ((*(t+j)) != '\0') {
*(s+i) = *(t+j);
/* ... */
works differently than
/* ... */
while ((*(s+i) = *(t+j)) != '\0') {
/* ... */
I hope you see it already, now that both cases stand side by side, actually vertically ;-). In the first case the value of t[j] is compared before it is copied to s[i]. In the second case the comparison is done after the copy. That's why the second case copies the terminating '\0' to the target string, and the first case does not.
The output you get works accidentally, it is Undefined Behavior, since you are writing beyond the border of the target array. Fortunately for you, both strings are laying in sequence in the memory, and you are overwriting the source string with its own characters.
Because your first case does not copy the '\0', the final printf() outputs more characters until a '\0' is encountered. By chance this is the last 'a'.
As others commented, the target string has not enough space for the concatenated string. Provide some more space like this:
char s[10] = "Hola"; /* 10 is enough for both strings and the terminating '\0'. */
However, if you had done this already, the error would have not been revealed, because the last 6 characters of s are initialized with '\0'. Not copying the terminating '\0' makes no difference. You can see this if you use
char s[10] = "Hola\0xxxx";
I don't think that your solution is the expected one. Instead of s[i] you are using *(s + i), which is essentially the same, accessing an array. Consider changing s (and in the course, t) in the function and use just *s.
Side note: The printf() in the function is most probably a leftover from debugging. But I'm sure you know.

Using Memmove to copy a string into itself produces wrong output

I'm given a string as such:
"Hello World"
With 5 Spaces characters inbetween the two words. I want to remove all but one of the spaces between the two words . However, my code seems to only work when there is 3 spaces or less. I'm using memmove to try and accomplish this.
Here is what I've tried:
int main(void) {
char * word = malloc(sizeof(char)*16);
strcpy(word,"Hello World");
checkWords(word);
return 0;
}
void checkWords(char * word) {
int i;
for(i=0; i < strlen(word); i++) {
if(word[i] == ' ' )
memmove(&word[i],&word[i+1],strlen(word)+1);
}
printf("The string without spaces is %s\n",word);
}
The output here is "Hello World"
Not "Hello World"
If It try input such as:
"Hello World" gets me "Hello World" -->correct
"Hello World" gets me "Hello World" -->correct
anything greater than 3 spaces, gets me incorrect output. (I want to have one space between the two words.
There are four problems. One is undefined behaviour: Take a 1,000,000 byte string where just the last character is a space. You will be moving about one megabyte after the end of the string one byte forward. That's quite fatal.
One is just a bug: You examine every character position in the string only once. If you have two consecutive spaces, you move the second space into the position of the first one and leave it there.
And one is a performance issue: If you fix the first two problems, and then you take a 1,000,000 byte string consisting of spaces only, you will do one million memmoves each moving between 0 and 1 megabyte. That's 500 gigabyte moved. That takes time.
And another performance issue is calling strlen in a loop. If your string is one million bytes, you do one million calls to strlen, and each call will go through the whole string until the end, scanning the whole megabyte string for the trailing zero byte.
PS. I misinterpreted what you are trying to achieve: Your code will delete any single space, and delete one out of any pairs of spaces. So it leaves one space for every two spaces you had. So it won't work if there is one space. It works by coincidence for two or three spaces. And if you have lots of spaces, it will keep about half of them.
Copying the "right" side of the string potentially multiple times is inefficient. Use of strcpy() or memcpy() is not an efficient approach.
Repeatedly calling strlen() is also inefficient.
Suggest using two indexes and walk them through the string.
#include <stdbool.h>
void RemoveExtraSpaces(char * word) {
if (word[0]) {
size_t src = 1;
size_t dest = 1;
do {
if (word[src] != ' ' || word[src - 1] != ' ') {
word[dest] = word[src];
dest++;
}
} while (word[src++]);
}
printf("The string without spaces is `%s`\n", word);
}
Unclear what should happen with multiple spaces before the first word or after the last word. This code shrinks those to 1 space.
After accept variation - slight simplification. Inspired by #Joachim Pileborg good answer.
void RemoveExtraSpaces2(char * word) {
size_t src = 0;
size_t dest = 0;
do {
if (word[src] == ' ' && word[src + 1] == ' ') {
src++;
}
word[dest] = word[src];
dest++;
} while (word[src++]);
printf("The string without spaces is `%s`\n", word);
}
As this is too close to being a duplicate of Replace multiple spaces by single space in C, (BTW: which did not accept the best answer IMO), I am making this community wiki.
This works for me
for(i=0; i < strlen(word); i++) {
if(word[i] == ' ' )
{
while (word[i+1] == ' ' && word[i+1] != '\0')
memmove(&word[i],&word[i+1], strlen(word)-i);
}
}

C—Infinite loop, I think?

I'm having an issue with a program in C and I think that a for loop is the culprit, but I'm not certain. The function is meant to take a char[] that has already been passed through a reverse function, and write it into a different char[] with all trailing white space characters removed. That is to say, any ' ' or '\t' characters that lie between a '\n' and any other character shouldn't be part of the output.
It works perfectly if there are no trailing white space characters, as in re-writing an exact duplicate of the input char[]. However, if there are any, there is no output at all.
The program is as follows:
#include<stdio.h>
#define MAXLINE 1000
void trim(char output[], char input[], int len);
void reverse(char output[], char input[], int len);
main()
{
int i, c;
int len;
char block[MAXLINE];
char blockrev[MAXLINE];
char blockout[MAXLINE];
char blockprint[MAXLINE];
i = 0;
while ((c = getchar()) != EOF)
{
block[i] = c;
++i;
}
printf("%s", block); // for debugging purposes
reverse(blockrev, block, i); // reverses block for trim function
trim(blockout, blockrev, i);
reverse(blockprint, blockout, i); // reverses it back to normal
// i also have a sneaking suspicion that i don't need this many arrays?
printf("%s", blockprint);
}
void trim(char output[], char input[], int len)
{
int i, j;
i = 0;
j = 0;
while (i <= len)
{
if (input[i] == ' ' || input[i] == '\t')
{
if (i > 0 && input[i-1] == '\n')
for (; input[i] == ' ' || input[i] == '\t'; ++i)
{
}
else
{
output[j] = input[i];
++i;
++j;
}
}
else
{
output[j] = input[i];
++i;
++j;
}
}
}
void reverse(char output[], char input[], int len)
{
int i;
for (i = 0; len - i >= 0; ++i)
{
output[i] = input[len - i];
}
}
I should note that this is a class assignment that doesn't allow the use of string functions, hence why it's so roundabout.
Change
for (i; input[i] == ' ' || input[i] == '\t'; ++i);
to
for (; i <= len && (input[i] == ' ' || input[i] == '\t'); ++i);
With the first method, if the whitespace is at the end, the loop will iterate indefinitely. Not sure how you didn't get an out of bounds access exception, but that's C/C++ for you.
Edit As Arkku brought up in the comments, make sure your character array is still NUL-terminated (the \0 character), and you can check on that case instead. Make sure you're not trimming the NUL character from the end either.
Declaring your main() function simply as main() is an obsolete style that should not be used. The function must be declared either as int main(void) or as int main(int argc, char *argv[]).
Your input process does not null-terminate your input. This means that what you're working with is not a "string", because a C string, by definition, is an array of char that the last element is a null character ('\0'). Instead, what you've got are simple arrays of char. This wouldn't be a problem as long as you're expecting that, and indeed your code is passing array lengths about, but you're also trying to print it with printf(), which requires C strings, not simple arrays of char.
Your reverse() function has an off-by-one error, because you aren't accounting for the fact that C arrays are zero-indexed, so what you're reversing is always one byte longer than your actual input.
What this means is that if you call reverse(output, input, 10), your code will start by assigning the value at input[10] to output[0], but input[10] is one past the end of your data, and since you didn't initialize your arrays before starting to fill them, that's an indeterminate value. In my testing, that indeterminate value happens, coincidentally, to have zero values much of the time, which means that output[0] gets filled with a null ('\0').
You need to be subtracting one more from the index into the input than you actually are. The loop-termination condition in the reverse() function is also wrong, in compensation, that condition should be len - i > 0, not len - i >= 0.
Your trim() function is unnecessarily complex. Additionally, it too has an incorrect loop condition to compensate for the off-by-one error in reverse(). The loop should be while ( i < len ), not while ( i <= len ).
Additionally, the trim() function has the ability to reduce the size of your data, but you don't provide a way to retain that information. (I see in the comments of Arkku's answer that you've corrected for this already. Good.)
Once you've fixed the issue with not keeping track of your data's size changes, and the off-by-one error which is copying indeterminate data (which happens, coincidentally, to be a null) from the end of the blockout array to the beginning of the blockprint array when you do the second reverse(), and you fix the incorrect <= condition in trim() and the incorrect >= condition in reverse(), and you null-terminate your byte array before passing it to printf(), your program will work.
(Moved from comments to an answer)
My guess is that the problem is outside this function, and is caused by the fact that in the described problem cases the output is shorter than the input. Since you are passing the length of the string as an argument, you need to calculate the length of the string after trim, because it may have changed...
For instance, passing an incorrect length to reverse can cause the terminating NUL character (and possibly some leftover whitespace) to end up at the beginning of the string, thus making the output appear empty.
edit: After seeing the edited question with the code of reverse included, in addition to the above problem, your reverse puts the terminating NUL as the first character of the reversed string, which causes it to be the empty string (in some cases your second reverse puts it back, so you don't see it without printing the output of the first reverse). Note that input[len] contains the '\0', not the last character of the string itself.
edit 2: Furthermore, you are not actually terminating the string in block before using it. It may be the case that the uninitialised array often happens to contain zeroes that serve to terminate the string, but for the program to be correct you absolutely need to terminate it with block[i] = '\0'; immediately after the input loop. Similarly you need ensure NUL-termination of the outputs of reverse and trim (in case of trim it seems to me that this already happens as a side-effect of having the loop condition i <= len instead of i < len, but it's not a sign of good code that it's hard to tell).

what is wrong with the while loop here ? C basics

I am trying to count the number of letters of a word before the space. For eg. "Hello how" is my input string and I am trying to count the number of letters in the first word.
#include<stdio.h>
#include<string.h>
int main()
{
char a[30];
int count = 0;
printf("Enter the string.\n"); // Enter hello how as string here.
gets(a);
for ( i = 0; a[i] != '\0'; i++ )
{
while( a[i+1] != ' ' )
count++;
}
printf("%d\n",count);
}
This is a small part of a bigger code, I am actually expecting the value of count to be 5 but it gets into some sort of infinite loop which I am unable to figure out. If I use if instead of while , I get the expected answer. I know gets is not very reliable and I will not use once I get better at programming so it will be kind of you to post your answer about the loop instead of gets. Thank You.
The NUL character is written with a backslash (\), not forward slash (/).
for (i = 0; a[i] != '\0'; i++)
Furthermore, the inner loop will not terminate because you're not incrementing i.
while (a[i] != ' ') {
i++;
count++;
}
Actually, you should not really have two loops. One loop is all you need.
for (i = 0; a[i] != '\0' && a[i] != ' '; i++) {
count++;
}
The expression within the while statement does not depend on count. So with every iteration of the while loop the count gets incremented, but that has no influence on the while conditional, hence it will loop ad infinitum or never, depending on the character at a[i+1].
In addition to that, the conditional statement for the for loop is not written correctly either. The string escape for a NUL character is \0 (backslash). Or you can just compare against a 0 literal, which has exactly the same outcome (though when it comes to the subtleties of C it does not mean exactly the same, but that's splitting hairs).
Use '\0' instead of '/0' in the for loop condition and also the while loop condition will never be false because i remains the same

How to erase every occurences of vowels in a string

In a schools assignment we are asked to remove every occurences of vowels from a string.
So:
"The boy kicked the ball" would result in
"Th by kckd th bll"
Whenever a vowel is found, all the subsequent characters somehow have to shift left, or at least that's my approach. Being that I just started learning C, it may very well be that it's a ridiculous approach.
What I'm trying to do is: When I hit the first vowel, I "shift" the next char ([i+1]) to the current pos (i). the shifting then has to continue for every subsequent character, so int startshift is set to 1 so the first if block excecutes on every subsequent iteration.
The first if block also test to see if the next char is a vowel. Without such a test any character preceding a vowel would "transform" to the adjacent vowel, and every vowel except the first would still be present. However this resulted in every vowel being replaced by the preceding char, hence the if else block.
Anyway, this ugly code is what I've come up with so far. (The names used for the char* pointers make no sense (I just don't know what to call them), and having two sets of them is probably redudant.
char line[70];
char *blank;
char *hlp;
char *blanktwo;
char *hlptwo;
strcpy(line, temp->data);
int i = 0;
int j;
while (line[i] != '\n') {
if (startshift && !isvowel(line[i+1])) { // need a test for [i + 1] is vowel
blank = &line[i+1]; // blank is set to til point to the value of line[i+1]
hlp = &line[i]; // hlp is set to point to the value of line[i]
*hlp = *blank; // shifting left
} else if (startshift && isvowel(line[i+1])) {
blanktwo = &line[i+1];
hlptwo = &line[i];
*hlptwo = *blanktwo;
//*hlptwo = line[i + 2]; // LAST MOD, doesn't work
}
for (j = 0; j < 10; j++) { // TODO: j < NVOWELS
if (line[i] == vowels[j]) { // TODO: COULD TRY COPY EVERYTHING EXCEPT VOWELS
blanktwo = &line[i+1];
hlptwo = &line[i];
*hlptwo = *blanktwo;
startshift = 1;
}
}
i++;
}
printf("%s", line);
The code doesn't work.
with text.txt:
The boy kicked the ball
He kicked it hard
./oblig1 remove test.txt produces:
Th boy kicked the ball
e kicked it hard
NB. I've omitted the outer while loop used for iterating the lines in the text file.
Just some food for thought, since this is homework and I don't want to spoil the fun:
You might also tackle this problem without using a second 'temp->data' buffer. If the given input string is in a modifiable memory chunk, like
char data[] = "The boy kicked the ball";
You could also write a program which maintains two pointers into the buffer:
One pointer points to the position in the string where the next vowel would need to be written; this pointer is advanced whenever a vowel was written.
The second pointer points to the position in the string where the next character to consider is read from; this pointer is advanced whenever a character is read.
If you think about it, you can see that the first pointer will not advance as fast as the second pointer (since every character is read, but not every character is written out - vowels are skipped).
If you go for this route, consider that you may need to terminate the string properly.
Try use std containers and objects
#include <iostream>
#include <string>
#include <vector>
std::string editStr = "qweertadoi";
std::vector<char> vowels{'i', 'o', 'u', 'e', 'a'};
int main() {
for(unsigned int i = 0; i<editStr.size(); i++){
for(char c: vowels){
if(editStr.at(i) == c){
editStr.erase(i--,1);
break;
}
}
}
std::cout << editStr << std::endl;
return 0;
}

Resources