Does this small C program satisfy the K&R exercise? - c

I'm on to K&R's Exercise 1-18
Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
This is what I've came up with so far
#include <stdio.h>
#define MAXLINE 1000
int getline(char line[], int maxline);
void copy(char to[], char from[]);
int main () {
int len;
char line[MAXLINE];
while (getline(line, MAXLINE) > 0) {
printf("%s", line);
}
return 0;
}
int getline(char s[], int lim) {
int c, i, lastNonBlankIndex;
lastNonBlankIndex = 0;
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
if (c != ' ' && c != '\t') {
lastNonBlankIndex = i + 1;
}
s[i] = c;
}
if (i != lastNonBlankIndex) {
i = lastNonBlankIndex;
c = '\n';
}
if (c == '\n') {
s[i] = c;
++i;
}
s[i] = '\0';
return i;
}
The second part sounded hard, as I wasn't sure what I should return if the line only has blanks or tabs. After all, if I return 0, it will halt the getline() calling. Would this be where I should set up a #define, such as ALL_BLANKS.
Anyway, to actual main question, is this a correct way to remove trailing blanks and tabs from lines? I ran a few inputs through, and it seemed to work. However, if I copy and paste text with newlines into the CL, it appears all strung together. And when I type a line into the CL and push enter, it automatically prints it. Should I be building an array of lines, and then looping through and printing them when done ?

Your code looks correct, but I think it would be better if you separate the operations of reading a line from stdin and stripping the line of trailing whitespace (decoupling). Then you can use the unmodified getline from the book (code reuse) and won't have the problem of halting on returning 0.
And if you are interested in other solutions, the CLC-wiki has an almost complete list of K&R2 solutions.
#include <stdio.h>
#define MAXLINE 1024
int getline(char s[], int lim);
main()
{
int i, len;
char line[MAXLINE];
while ((len = getline(line, MAXLINE)) > 0) {
i = len - 2;
while (i >= 0 && (line[i] == ' ' || line[i] == '\t'))
--i;
if (i >= 0) {
line[i+1] = '\n';
line[i+2] = '\0';
printf("%s", line);
}
}
return 0;
}
This is the category 1 solution I wrote some time ago. getline is as on page 28 of the book. It might be nicer to put the removal of whitespace in a separate function rstrip, but I leave this as an exercise for the reader.

Your basic design is sound. It is better, as you did, to print a stripped line as soon as you've built it, so that your program only needs to keep one line at a time in memory and not the whole file.
There is a small problem with your code: it doesn't implement the second part of the question (“delete entirely blank line”). That's because you always tack a '\n' at the end of the string. This is easy to fix, but remember that you must return a nonzero value to your caller since a blank line doesn't indicate the end of the file.

getline should return -1 (a negative value in general) if there is an error or if EOF is reached. Then your loop conditional can check that it returns something >= 0 and still allow for 0 length lines.
for (i=0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
I almost never include an assignment within a loop conditional. I would rather add 10 lines of code to get around doing that because it's difficult to read. I would especially refrain from using them with complicated conditionals.
int i = 0;
while (i < lim) {
c = getchar();
if (c == EOF || c == '\n') {
break;
}
line[i] = (char)c;
i++;
}
line[i] = '\0'; // Null terminate the string
This code should read in a line for you. I would separate the reading in of the line from the removal of the trailing white space. You could very easily work backwards from the end of the string to remove white spaces at the location where I null terminated the line, since after having read in the line you now know its length. Essentially you grow the string and then you prune it back down after it has finished growing.

This is how i did it.
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 0
int state = OUT;
int getline(char s[], int lim);
void copy(char to[], char from[]);
int main(void)
{
int lenght;
int max = 0;
char line[MAXLINE];
char longest[MAXLINE];
while ((lenght = getline(line, MAXLINE)) > 0)
if (lenght > max)
{
max = lenght;
copy(longest, line);
}
if (max > 0)
printf("\n%s\n", longest);
return 0;
}
int getline(char s[], int lim)
{
int i, c;
for (i = 0; i < lim - 1 && ((c = getchar()) != EOF) && (c != '\n'); i++)
{
if (state == IN && c != ' ' && c != '\t')
{
s[i] = ' ';
i++;
state = OUT;
}
if (s[0] == ' ')
{
s[0] = '\b';
}
s[i] = c;
if (c == ' ' || c == '\t')
{
i--;
state = IN;
}
}
if (c == '\n')
{
s[i] = c;
i++;
}
s[i] = '\0';
return i;
}
void copy(char to[], char from[])
{
int i = 0;
while ((to[i] = from[i]) != '\0')
i++;
}

#include <stdio.h>
#define MAXLINE 1000
size_t getline(char *s,size_t lim)
{
if( fgets(s,lim,stdin) )
{
while( *s && strchr(" \t\n",s[strlen(s)-1]) )
s[strlen(s)-1]=0;
return strlen(s);
}
return 0;
}
main()
{
int len;
char line[MAXLINE];
while (getline(line,sizeof line)) {
printf("%s", line);
}
return 0;
}

Related

Kernighan and Ritchie C exercise 1-18

I was trying to complete the task requested by this exercise from the K&R book:
Write a program to remove trailing blanks and tabs from each line of input, and to delete entirely blank lines.
I still have to figure out how to implement the first task, but I have this idea for the second one:
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 1
int blank1(char s[], int len){
int i;
int c=0;
for (i=0; i<len;i++){
if(s[i] == ' ' | s[i] == '\t')
c++;
}
if (c==(len-1))
return 1;
else
return 0;
}
int get_line1(char s[], int lim){
int c,i;
for (i=0; i<lim-1 && ((c=getchar())!= EOF) && c!='\n'; i++){
s[i]=c;
}
if (c=='\n'){
s[i]=c;
i++;
}
s[i] = '\0';
return i;
}
int main() {
char line[MAXLINE];
int len;
int j=1;
int i;
while(j){
len=get_line1(line,MAXLINE);
if ((blank1(line,len)) == 0) {
printf("%s", line);
}
if (getchar() == EOF){
j=0;
}
}
}
The function blank1 takes as input a string s[] and the string's length len.
Then it cycles for all the string and increases the counter i every time it hits a blank or tab. If the string is completely made of blank or tab, such as \t\t\t\n\0, then the value of i will be the same value of the length of the string - 1 (so to speak, the length of the string excluding the character \n). If the string is completely blank, then 1 will be returned, otherwise 0. So in this way if blank(string,string's length) returns 1, we know it is a wholly blank string and we can avoid printing it, as the exercise requests.
The problem is that with some outputs this program cuts the first letter. For instance with:
Once upon a time\n
There was a little monkey\n
That\n
What is printed is:
Once upon a time
here was a little monke
hat
I can't manage to get why this truncation occurs.
EDIT:
#include <stdio.h>
#define MAXLINE 1000
#define IN 1
#define OUT 1
int blank1(char s[], int len){
int i;
int c=0;
for (i=0; i<len;i++){
if(s[i] == ' ' | s[i] == '\t')
c++;
}
if (c==(len-1))
return 1;
else
return 0;
}
int get_line1(char s[], int lim){
int c,i;
for (i=0; i<lim-1 && ((c=getchar())!= EOF) && c!='\n'; i++){
s[i]=c;
}
if (c==EOF)
return -1; /////
if (c=='\n'){
s[i]=c;
i++;
}
s[i] = '\0';
return i;
}
int main() {
char line[MAXLINE];
char verso[MAXLINE];
char longest[MAXLINE];
int len;
int j=1;
int i;
while(j){
len=get_line1(line,MAXLINE);
if (len==-1){ ///////////
j=0;
}
else if ((blank1(line,len)) == 0) {
printf("%s", line);
}
}
}
getchar() == EOF in main is swallowing your first character from each line (after the first). You are better off calling getchar in only one place.
One possibility that causes minimal churn to your current implementation is to keep the only getchar call inside get_line1 and return -1 from get_line1 if a EOF is read there, to then be handled inside main, instead of the call to getchar inside main.
Do not program in main use functions for similar tasks.
char *skipLeadingBlanks(char *str)
{
char *wrk = str;
while((*wrk && *wrk != '\n') && (*wrk == ' ' || *wrk == '\t')) *wrk++;
memmove(str, wrk, strlen(wrk) + 1);
return str;
}
int main(void)
{
char str[1024];
while(fgets(str, sizeof(str), stdin))
{
skipLeadingBlanks(str);
if(!*str || *str == '\n') continue; //empty line
printf("%s",str);
}
}
https://godbolt.org/z/hxW5zP5Mx

Write a program to break long input lines into two or more shorter lines of length at most n

I am currently learning C and working on a problem that breaks input lines into lengths of n. Below is my current code where n is set to 30. When it reaches the n-th index it replaces that index with ' ' and then line breaks, but it will only do it for the first n characters and I'm unsure what isn't getting rest in order to it to continue making a new line at the nth index.
int getline2(void);
int c, len, cut, counter;
char line[MAXLINE];
main() {
while ((len = getline2()) > 0) {
if (len > BREAK) {
c = 0;
counter = 0;
while (c < len) {
if (line[c] == ' ') {
counter = c;
}
if (counter == BREAK) {
line[counter] = '\n';
counter = 0;
}
counter++;
c++;
}
}
printf("%s", line);
}
return 0;
}
int getline2(void) {
int c, i;
extern char line[];
for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
line[i] = c; //i gets incremented at the end of the loop
if (c == '\n') {
line[i] = c;
++i;
}
line[i] = '\0';
return i;
}
Your code is a little too complicated:
you do not need to store the bytes read from the file into an array, just output them one at a time, keeping track of the line length
when the line would become too long, output a newline and reset the count before you output the byte.
also not that none of these global variables deserves to be global.
and the prototype for main should be either int main(), int main(void) or int main(int argc, char *argv[]) or equivalent. main()` is an obsolete syntax that should be avoided.
Here is a modified version:
#include <stdio.h>
#define BREAK 30
int main() {
int c;
int len = 0;
while ((c = getchar()) != EOF) {
if (c == '\n') {
putchar(c);
len = 0;
} else {
if (len >= BREAK) {
putchar('\n');
len = 0;
}
putchar(c);
len++;
}
}
return 0;
}

Testing stored array characters in C

I have just started learning C and I am trying to complete an exercise where I have to remove all the trailing whitespace characters in a line. My logic seem fine to me, but when I try to test if a character is a tab or a space it doesn't come back true, even if it is.
The part with the problem:
if ((s[ii] == ' ' || s[ii] == '\t') && trailing == 1)
The entire thing:
#include <stdio.h>
#include <ctype.h>
#define MAXLINE 1000 /* maximum input line length */
int getline2(char line[], int maxline, char outputLine[]);
/* print above 80 character lines */
main()
{
char cl;
char sl[MAXLINE];
char ol[MAXLINE];
int i;
while((cl = getline2(sl, MAXLINE, ol)) > 0){
if(cl > 1){
printf("%d: %s", cl, ol);
}
}
}
/* getline: read a line into s, return length */
int getline2(char s[], int lim, char ss[])
{
int c, i, ii;
for (i=0; i < lim-1 && (c=getchar())!=EOF && c!= '\n'; ++i){
s[i] = c;
}
int trailing = 1;
for(ii = i; ii >= 0; ii--){
if ((s[ii] == ' ' || s[ii] == '\t') && trailing == 1){
i--;
} else {
ss[ii] = s[ii];
trailing = 0;
}
}
if (c == '\n') {
ss[i] = c;
i++;
}
ss[i] = '\0';
return i;
}
This for loop should start from i - 1:
for(ii = i - 1; ii >= 0; ii--){
^^^^^
because in previous for loop, i is incremented after adding the trailing spaces and tabs in the string s.

My program can't output the lines correctly

I'm relatively new in C and I currently reading Kernighan's book.
One of the problems in the book is to create an algorithm that from a input line output the line if it is more than 10 characters long.
The point is I'm frustrated because I cant find what is wrong with my code. I debugged and recreate it many times but still cant find out what's going on!
The escape character from function getl() is '.' (dot), and sometimes works and other times don't. If you compile it and test you will see:
gcc -Wall -o out 'script.c'
The question header from the book is:
“Exercise 1-17. Write a program to print all input lines that are longer than 10 characters.”
I'm sure that's relatively easy, but I really wanted to know why this algorithm is not working as expected, i think it has something to do with '\n'.
If someone could help me find out what's the problem with the code, I would appreciate it.
Code
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c;
int count;
for (count = 0 ; count < lim-1 && (c = getchar()) != '.' ; count++) {
if (c == '\n') {
line[count] = '\n';
count++;
break;
}
line[count] = c;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
getl(line, MAX);
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
Your code almost works. You just seem to have some repeated lines here and there that confuse things.
Specifically, you are calling getl(line, MAX); twice in a row. The first gets the input, but don't save the count, the second has only an empty stdin buffer to work with so no sensible count is saved from that. Removing the first call that don't save the count fixes your issue.
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c = getchar();
int count;
for (count = 0 ; c != '.' ; count++) {
line[count] = c;
c = getchar();
}
line[count++] = '\n';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
First, you're calling your getl function twice instead of once (you only want to read lines one by one). Fixing that should work.
Then I think you shouldn't add the trailing '\n' to your lines, just print it when your line is longer than 10 characters, in your code, the '\n' will be counted as a character.
Here's the modified code:
#include <stdlib.h>
#include <stdio.h>
#define MAX 10000
int getl(char line[])
{
char c;
int count;
for (count = 0; count < MAX - 1 && (c = getchar()) != '.' ; count++)
{
if (c == '\n')
break;
line[count] = c;
}
line[count] = '\0';
return (count);
}
int main()
{
char line[MAX];
int len = 1;
while (len > 0)
{
len = getl(line);
if (len >= 10)
printf("%s, c = %i\n", line, len);
}
return (0);
}
This should work. https://ideone.com/cXXRUH
#include <stdio.h>
#define MAX 10000
int getline_length(char line[]) {
char ch;
int count = 0;
printf("\nWaiting for INPUT...");
// Using clear while loop to get input, removing redundent complexity
// Either `.` or `\n` consider End Of Line
while(count < MAX-1 && ((ch = getchar()) != '.' || (ch = getchar()) != '\n')) {
line[count++]=ch;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
while(1) {
// reset array before each input
memset(line, 0, sizeof(line));
int len = getline_length(line); //No need to pass limit
if (len >= 10) {
printf("%s", line);
} else {
printf("len < 10");
}
}
return 0;
}

In k&r book exercise 1.18 write a program to remove trailing blanks and tabs from each line of input,and to delete entirely blank lines

#include <stdio.h>
#define MAXLINE 1024
int getline(char s[], int lim);
main()
{
int i, len;
char line[MAXLINE];
while ((len = getline(line, MAXLINE)) > 0) {
i = len - 2;
while (i >= 0 && (line[i] == ' ' || line[i] == '\t'))
--i;
if (i >= 0) {
line[i+1] = '\n';
line[i+2] = '\0';
printf("%s", line);
}
}
return 0;
}
int getline(char s[],int lim)
{
int i,c;
for(i=0;i<lim-1 && (c=getchar())!=EOF && c!='\n';++i)
s[i] = c;
if( c == '\n')
{
s[i]=c;
++i;
}
s[i]='\0';
return i;
}
This is my program which i tired but it seems like displaying the same input without any changes.I can't able to understand what mistake i did ....
Thanks in advance.
Your getline function increments i (the length returned) for the '\n', but not for the terminating '\0'.
Hence, in i = len - 2; you should be subtracting one, not two... right? To clarify, I'm suggesting that you try i = len - 1;!
Here is a couple of hopefully helpful hints.
There is already a function getline(), you are overruling that name. If you increase your compilation warning level then you'll probably see the warning. Anyway, assuming you want to have your own line-reading function, you could change it to say my_getline(). If you do this, then your program works for me.
Similarly, your declaration of main() doesn't have the type in front of it as in int main(). That's OK, the default type is int, but for neatness' sake, you should put the type in there (again, to avoid compilation warnings when you increase the warning level).
Your program can't handle lines longer than 1024 characters. That's ok if you can live with this limitation. If you would want to get rid of this limitation, then you'd want to wire my_getline() in such a way that it would return a pointer to allocated memory holding the entire line of text that it read. In main() you could strip your trailing space etc. and then free() the memory. In this case the prototype of my_getline() would be of course char *my_getline().
Check for the number of characters after removing white-space characters(blanks , tabs ) then compare with no of characters before removing the white-spaces . if they differ your program works..
you will not be able to see the difference in our input and out since we are dealing with white space characters here..
The following solution works for me. Please note that I have not defined any new functions.
#include <stdio.h>
#define MAXLINE 1000
#define INLINE 1
#define OUTLINE 0
int main(void) {
int i = 0, j, c;
char line[MAXLINE];
int status = OUTLINE;
while((c = getchar()) != EOF) {
if (c != '\n') {
line[i] = c;
i++;
} else if (c == '\n'){
line[i] = '\n';
line[i+1] = '\0';
for (j = i; j >= 0 && status == OUTLINE; j--) {
char lc = line[j];
if (lc == ' ' || lc == '\t') {
line[j] = '\n';
line[j+1] = '\0';
status = INLINE;
}
}
printf("%s", line);
for (j = 0; j < MAXLINE; j++) {
line[j] = 0;
}
i = 0;
}
}
return 0;
}

Resources