K&R c programming book 3.7 trim function example - c

I've been following K&R book 2nd version for c programming, but in 3.7 part, here is the screenshot of the function and my code part:
#include <stdio.h>
#include <string.h>
/* trim: removing the trailing blanks, tabs and newlines */
int trim(char s[]);
int main(){
char s[] = "hello,world \t\t\t\t\n\n\n\n";
printf("%s\n", s);
int length = trim(s);
printf("%d\n", length);
return 0;
}
int trim(char s[]){
int n;
for (n = strlen(s) -1 ; n >= 0 ; n--)
if (s[n] != ' ' && s[n] != '\n' && s[n] != '\t')
break;
s[n+1] = '\0';
return n;
}
The following is the output I get from running it:
Obviously the result length is 11, "hello,world", but the program outputs 10,
the reason is s[n+1] = '\n', instead of s[++n];I think it should be s[++n],
otherwise I will get the wrong output ==> 10, how to deal with it? could anyone plz take a look at it?

I would iterate over length and look ahead (behind?) one element to see if we need to trim it:
int trim(char s[]) {
int n;
for(n = strlen(s); n > 0; n--)
if(s[n-1] != ' ' && s[n-1] != '\t' && s[n-1] != '\n')
break;
s[n] = '\0';
return n;
}

In my opinion the book does not cleanly define the meaning of the return value.
But nearly:
looking for the first character that is not ...
I think that returning 10 matches that, if it is to be considered the definition.
So the 10 actually seems to be the correct value to me, it is the position of the "first" (or "last") non-white space character before/from the end of the string.
To answer the question "How to deal with it?":
You might not actually care, if you only use the modified string and not the return value.
If you do use the return value you need to use it according to the definition (if you consider the quoted text to be the definition), i.e. in order to get the length you have to increment by one in the using/calling function (not in the called function).
Or you modify the function as you described. That option only exists if you "own" the code (which in your own project and tutorials you do...). Keep in mind however, that this option will usually NOT be available in professional context of larger projects. There the APIs and behaviour is defined elsewhere and cannot be changed locally.
I do by the way agree and feel with you (your implicit rejection of the probable definition) and at least one commenter. The return value definition "position of last non-white character" is not one which jumps to mind....

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.

Loop through user input with getchar

I have written a small script to detect the full value from the user input with the getchar() function in C. As getchar() only returns the first character i tried to loop through it... The code I have tried myself is:
#include <stdio.h>
int main()
{
char a = getchar();
int b = strlen(a);
for(i=0; i<b; i++) {
printf("%c", a[i]);
}
return 0;
}
But this code does not give me the full value of the user input.
You can do looping part this way
int c;
while((c = getchar()) != '\n' && c != EOF)
{
printf("%c", c);
}
getchar() returns int, not char. And it only returns one char per iteration. It returns, however EOF once input terminates.
You do not check for EOF (you actually cannot detect that instantly when getchar() to char).
a is a char, not an array, neither a string, you cannot apply strlen() to it.
strlen() returns size_t, which is unsigned.
Enable most warnings, your compiler wants to help you.
Sidenote: char can be signed or unsigned.
Read a C book! Your code is soo broken and you confused multiple basic concepts. - no offense!
For a starter, try this one:
#include <stdio.h>
int main(void)
{
int ch;
while ( 1 ) {
ch = getchar();
x: if ( ch == EOF ) // done if input terminated
break;
printf("%c", ch); // %c takes an int-argument!
}
return 0;
}
If you want to terminate on other strings, too, #include <string.h> and replace line x: by:
if ( ch == EOF || strchr("\n\r\33", ch) )
That will terminate if ch is one of the chars listed in the string literal (here: newline, return, ESCape). However, it will also match ther terminating '\0' (not sure if you can enter that anyway).
Storing that into an array is shown in good C books (at least you will learn how to do it yourself).
Point 1: In your code, a is not of array type. you cannot use array subscript operator on that.
Point 2: In your code, strlen(a); is wrong. strlen() calculates the length of a string, i.e, a null terminated char array. You need to pass a pointer to a string to strlen().
Point 3: getchar() does not loop for itself. You need to put getchar() inside a loop to keep on reading the input.
Point 4: getchar() retruns an int. You should change the variable type accordingly.
Point 5: The recommended signature of main() is int main(void).
Keeping the above points in mind,we can write a pesudo-code, which will look something like
#include <stdio.h>
#define MAX 10
int main(void) // nice signature. :-)
{
char arr[MAX] = {0}; //to store the input
int ret = 0;
for(int i=0; i<MAX; i++) //don't want to overrrun array
{
if ( (ret = getchar())!= EOF) //yes, getchar() returns int
{
arr[i] = ret;
printf("%c", arr[i]);
}
else
;//error handling
}
return 0;
}
See here LIVE DEMO
getchar() : get a char (one character) not a string like you want
use fgets() : get a string or gets()(Not recommended) or scanf() (Not recommended)
but first you need to allocate the size of the string : char S[50]
or use a malloc ( #include<stdlib.h> ) :
char *S;
S=(char*)malloc(50);
It looks like you want to read a line (your question mentions a "full value" but you don't explain what that means).
You might simply use fgets for that purpose, with the limitation that you have to provide a fixed size line buffer (and handle - or ignore - the case when a line is larger than the buffer). So you would code
char linebuf[80];
memset (linebuf, 0, sizeof(linbuf)); // clear the buffer
char* lp = fgets(linebuf, sizeof(linebuf), stdin);
if (!lp) {
// handle end-of-file or error
}
else if (!strchr(lp, '\n')) {
/// too short linebuf
}
If you are on a POSIX system (e.g. Linux or MacOSX), you could use getline (which dynamically allocates a buffer). If you want some line edition facility on Linux, consider also readline(3)
Avoid as a plague the obsolete gets
Once you have read a line into some buffer, you can parse it (e.g. using manual parsing, or sscanf -notice the useful %n conversion specification, and test the result count of sscanf-, or strtol(3) -notice that it can give you the ending pointer- etc...).

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).

Taking a string as input and storing them in a character array in C [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions must demonstrate a minimal understanding of the problem being solved. Tell us what you've tried to do, why it didn't work, and how it should work. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I am stumped on how to store strings in an array in C, with each character kept separately. As an example, if the user inputs hellop, I want to store it in a given array, say userText, with userText[0] = h, userText[1] = e, userText[2] = l, and so on. I know this is easy stuff, but I'm still new. So if anyone could help, it would be great. Please explain how to do this using pointers.
#include<stdio.h>
void main()
{
char a[10],c;
int i=0;
while((c=getchar())!='\n')
{
scanf("%c",&a[i++]);
c=getchar();
}
for(i=0;i<11;i++)
printf("%c",a[i]);
}
The program outputs some garbage value (eoeoeoeo\363) when I type in hellop.
To read input I recommend using the fgets function. It's a nice, safe alternative to scanf.
First let's declare a buffer like so:
char user_input[20];
Then we can get user input from the command line in the following manner:
fgets(user_input, 20, stdin);
This will store a maximum of 20 characters into the string from the standard input and it will ensure it is null-terminated. The fact that we've limited the input to the size of the array declared earlier ensures that there's no possibility of buffer overruns.
Then let's clear the pesky newline that's been entered into the string using strlen:
user_input[strlen(user_input) -1] = '\0';
As strlen returns the size of the string up to the null terminator but without it, we can be sure at that position lies the newline character (\n). We replace it with a null-terminator(\0) so that the string ends there.
Finally, let's print it using printf:
printf("The user has entered '%s'\n", user_input);
To use fgets and printf you will need to declare the following header:
#include <stdio.h>
For strlen we need another header, namely:
#include <string.h>
Job done.
P.S. If I may address the code you've added to your question.
main is normally declared as int main rather than void main which also requires that main returns a value of some sort. For small apps normally return 0; is put just before the closing brace. This return is used to indicate to the OS if the program executed successfully (0 means everything was OK, non-zero means there was a problem).
You are not null-terminating your string which means that if you were to read in any other way other than with a careful loop, you will have problems.
You take input from the user twice - once with getchar and then with scanf.
If you insist on using your code I've modified it a bit:
#include<stdio.h>
int main()
{
char a[10];
int i=0;
while( (a[i++]=getchar()) != '\n' && i < 10) /* take input from user until it's a newline or equal to 10 */
;
a[i] = '\0'; /* null-terminate the string */
i = 0;
while(a[i] != '\0') /* print until we've hit \0 */
printf("%c",a[i++]);
return 0;
}
It should now work.
To read a string into char array:
char *a = NULL;
int read;
size_t len;
read = getline(&a, &len, stdin);
//free memory
free(a);
Your code is this (except I've added a bunch of spaces to improve its readability):
1 #include <stdio.h>
2 void main()
3 {
4 char a[10], c;
5 int i = 0;
6 while ((c = getchar()) != '\n')
7 {
8 scanf("%c", &a[i++]);
9 c = getchar();
10 }
11 for (i = 0; i < 11; i++)
12 printf("%c", a[i]);
13 }
Line-by-line analysis:
OK (now I've added the space between #include and <stdio.h>).
The main() function returns an int.
OK (it is hard to get an open brace wrong).
Since the return value of getchar() is an int, you need to declare c separately as an int.
OK.
Needs to account for EOF; should be while ((c = getchar()) != EOF && c != '\n'). You're still very open to buffer overflow, though.
OK.
Not OK. This reads another character from standard input, and doesn't check for EOF.
Not OK. This too reads another character from standard input. But when you go back to the top of the loop, you read another character. So, as things stand, if you type abcdefg at the program, c is assigned 'a' in the loop control, then a[0] is assigned 'b', then c is assigned 'c', then the loop repeats with a[1] getting 'e'. If I'd typed 6 characters plus newline, the loop would terminate cleanly. Because I claimed I typed 7 characters, the third iteration assigns 'g' to c, which is not newline, so a[2] gets the newline, and the program waits for more input with the c = getchar(); statement at the end of the loop.
OK (ditto close braces).
Not OK. You don't take into account early termination of the loop, and you unconditionally access a non-existent element a[10] of the array a (which only has elements 0..9 — C is not BASIC!).
OK.
You probably need to output a newline after the for loop. You should return 0; at the end of main().
Because your input buffer is so short, it will be best to code a length check. If you'd used char a[4096];, I'd probably not have bothered you about it (though even then, there is a small risk of buffer overflow with potentially undesirable consequences). All of this leads to:
#include <stdio.h>
int main(void)
{
char a[10];
int c;
int i;
int n;
for (i = 0; i < sizeof(a) && ((c=getchar()) != EOF && c != '\n')
a[i++] = c;
n = i;
for (i = 0; i < n; i++)
printf("%c", a[i]);
putchar('\n');
return 0;
}
Note that neither the original nor the revised code null terminates the string. For the given usage, that is OK. For general use, it is not.
The final for loop in the revised code and the following putchar() could be replaced (safely) by:
printf("%.*s\n", n, a);
This is safe because the length is specified so printf() won't go beyond the initialized data. To create a null terminated string, the input code needs to leave enough space for it:
for (i = 0; i < sizeof(a)-1 && ((c=getchar()) != EOF && c != '\n')
a[i++] = c;
a[i] = '\0';
(Note the sizeof(a)-1!)

Cannot figure this out C programming

The program is supposed to remove everything but the letters and create a new string which will have only the letters in upper-case.
However, it is not printing the results.
Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *remove_up(char input[])
{
char *new_str = (char *) malloc(strlen(input) + 1);
int i=0;
int j=0;
while (i < strlen(input))
{
if (((input[i]) >= 65 && (input[i]<=90)) || ((input[i]>=97) && (input[i]<=122)))
{
new_str[j]= toupper(input[i]);
i++;
j++;
}
else i++;
}
return new_str;
}
int main()
{
char str_1[100];
char str_2[100];
printf("Enter first word: ");
fgets(str_1, sizeof(str_1), stdin);
printf("Enter second word: ");
fgets(str_2, sizeof(str_2), stdin);
char *up_str_1 =(char *) malloc(strlen(str_1) + 1);
char *up_str_2 =(char *) malloc(strlen(str_2) + 1);
up_str_1= remove_up(str_1);
up_str_2= remove_up(str_2);
printf("%s", up_str_1);
printf("\n");
printf("%s", up_str_2);
return 0;
}
There are a few problems, but because this is tagged homework, I'll point them out but not give you the answer.
First of all, this doesn't do what you think:
int i, j = 0;
j will be initialized, but i probably won't start at 0. You need to initialize i to 0 as well.
Next, there's a typo - you missed a closing ] at (input[i<=122).
Finally, based on your answers to the questions, you probably aren't printing the result anyway: look up printf() or cout or whatever you prefer to use for outputting values.
It doesn't print results because you haven't used any print statements to show what comes back from your calls to remove_up.
To understand what is going on in your remove_up function, you need to understand this:
http://www.asciitable.com/
This code:
if (((input[i]) >= 65 && (input[i]<=90)) || ((input[i]>=97) && (input[i<=122)))
Is checking to see if a character is an alphabetic character in the ascii character set between these two ranges. Look at the link above. If it is in this set it's converting it to upper (redundant for half the data) and saving the result in your newly malloc'd string.
Problems:
1. You never set a null terminator in "new_str"
2. You never seem to free anything (though in this code it is trivial, in real code you could create problems, i.e. memory leaks).
3. "i" is redundant in the while loop. It's in both the if and else...
4. Rethink how you're using malloc (you probably don't want to use it this way in your custom functions unless you're going to cleanup after yourself)
There is probably more I'm missing, but that should help you see some problems.
Double check your use of parenths - you have more than needed. You are also missing a ']' in that if statement. Surprised it compiles.
change int i, j = 0; to int i = 0, j = 0;. Your i was initialized with a garbage value greater than strlen(input), and hence never entered the while loop.

Resources