The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 1.16 on page 30
Revise the main routine of the longest-line program so it will correctly print the length of arbitrarily long input lines, and as much as possible of the text.
Solution by Vidhan Gupta
/* Revise the main routine of the longest-line program so it will corretly print the lenght of arbitrarily long input lines, and as much as possible of the text. */ #include <stdio.h> #define MAXIMUM 22 int getLine(char line[], int lim); void copy(char to[], char from[]); int main() { int len; int max; char line[MAXIMUM]; char longest[MAXIMUM]; max = 0; while ((len = getLine(line, MAXIMUM)) > 0) if (len > max) { max = len; copy(longest, line); } if (max > 0) printf("%s%d\n", longest, max); return 0; } int getLine(char s[], int lim) { int i, j, c; for (i = 0, j = 0; (c = getchar()) != EOF && c != '\n'; ++i) if (i < lim - 1) { s[j] = c; j++; } if (c == '\n') { s[j] = c; j++; } s[j] = '\0'; return i; } void copy(char to[], char from[]) { int i = 0; while ((to[i] = from[i]) != '\0') i++; }
INPUT: What the hell is going on here And what are you doing I'm jsut programming for fun and gaining knowledge OUTPUT: I'm jsut programming 50
Solution by nits99
#include <stdio.h> #define MAXLENGTH 20 int getline(char [],int); void copy(char [],char []); int main() { int len,max=0; char line[MAXLENGTH],longest[MAXLENGTH]; while((len=getline(line,MAXLENGTH))>0) if(len>max){ max=len; copy(longest,line); } if(max>0){ if(max>MAXLENGTH){ printf("\n\nStorage limit exceeded by : %d",max-MAXLENGTH); printf("\nString length : %d",max); printf("\n%s",longest); } else printf("%s",longest); } printf("\n"); return 0; } int getline(char line[],int limit) { int i,c; for(i=0;i<limit-1&&(((c=getchar())!=EOF)&&(c!='\n'));i++) line[i]=c; if(i==(limit-1)){ while((c=getchar())!='\n'){ ++i; } } if(c=='\n'){ line[i]=c; ++i; } line[i]='\0'; return i; } void copy(char to[],char from[]) { int i=0; while((to[i]=from[i])!='\0') ++i; }
Revision by [wt70707] to solution of [nits99]
/* The above getline function got the following error if there is line great than limit characters * *** stack smashing detected ***: ./a.out terminated * Aborted (core dumped) * This is because i is increased to out of the bound of line and largest * */ int getline(char line[],int limit) { int i,c; for(i=0;(i<limit-1)&&(((c=getchar())!=EOF)&&(c!='\n'));i++) line[i]=c; if(i==(limit-1)) { line[i]='\0'; while(((c=getchar())!=EOF)&&(c!='\n')) { ++i; } } else { if(c=='\n') { line[i]=c; ++i; } line[i]='\0'; } return i; }
Solutions by Richard Heathfield and 386sx
/* This is the first program exercise where the spec isn't entirely * clear. The spec says, 'Revise the main routine', but the true * length of an input line can only be determined by modifying * getline. So that's what we'll do. getline will now return the * actual length of the line rather than the number of characters * read into the array passed to it. */ #include <stdio.h> #define MAXLINE 1000 /* maximum input line size */ int getline(char line[], int maxline); void copy(char to[], char from[]); /* print longest input line */ int main(void) { int len; /* current line length */ int max; /* maximum length seen so far */ char line[MAXLINE]; /* current input line */ char longest[MAXLINE]; /* longest line saved here */ max = 0; while((len = getline(line, MAXLINE)) > 0) { printf("%d: %s", len, line); if(len > max) { max = len; copy(longest, line); } } if(max > 0) { printf("Longest is %d characters:\n%s", max, longest); } printf("\n"); return 0; } /* getline: read a line into s, return length */ int getline(char s[], int lim) { int c, i, j; for(i = 0, j = 0; (c = getchar())!=EOF && c != '\n'; ++i) { if(i < lim - 1) { s[j++] = c; } } if(c == '\n') { if(i <= lim - 1) { s[j++] = c; } ++i; } s[j] = '\0'; return i; } /* copy: copy 'from' into 'to'; assume 'to' is big enough */ void copy(char to[], char from[]) { int i; i = 0; while((to[i] = from[i]) != '\0') { ++i; } }
Solution by Mr. "386sx"
Chris Sidi, however, was not convinced - he thought this answer
was "too easy", so he checked with bwk, who agreed. Chris writes:
"Looks like Mr. Kernighan meant for "main routine" in Exercise 1-16 to
refer to function main(), saying your solution of modifying getline() is
"too easy." :) (Though I think your solution shouldn't be removed from
the Answers web site, just complimented with another one that only
modifies main())"
Comment: On reflection, maybe this should have been somewhat more obvious. After describing the functioning of the longest line code and C's storage of string constants in character arrays, the following paragraph appears shortly before Exercise 1-16 is posed:
It is worth mentioning in passing that even a program as small as this one presents some sticky
design problems. For example, what should main
do if it encounters a line which is bigger than
its limit? getline
works safely, in that it stops collecting when the array is full, even if no
newline has been seen. By testing the length and the last character returned, main
can
determine whether the line was too long, and then cope as it wishes. In the interests of brevity,
we have ignored this issue.
Cue Mr "386sx", riding to the rescue on a white horse...
/* Exercise 1-16 */ #include <stdio.h> #define MAXLINE 20 int getline(char s[], int lim); void copy(char to[], char from[]); int main(void) { char line[MAXLINE]; char longest[MAXLINE]; char temp[MAXLINE]; int len, max, prevmax, getmore; max = prevmax = getmore = 0; while((len = getline(line, MAXLINE)) > 0) { if(line[len - 1] != '\n') { if(getmore == 0) copy(temp, line); prevmax += len; if(max < prevmax) max = prevmax; getmore = 1; } else { if(getmore == 1) { if(max < prevmax + len) { max = prevmax + len; copy(longest, temp); longest[MAXLINE - 2] = '\n'; } getmore = 0; } else if(max < len) { max = len; copy(longest, line); } prevmax = 0; } } if(max > 0) { printf("%s", longest); printf("len = %d\n", max); } return 0; } int getline(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; } else if(c == EOF && i > 0) { /* gotta do something about no newline preceding EOF */ s[i] = '\n'; ++i; } s[i] = '\0'; return i; } void copy(char to[], char from[]) { int i; i = 0; while((to[i] = from[i]) != '\0') ++i; }
Solution by arnuld
I think "Revising the main routine" means, just modifying the "main" function and that is what I did. I did not change anything in "getline" or "copy" functions.
/* K&R2: 1.9, Character Arrays, exercise 1.16 STATEMENT: revise the main routine of the longest-line programme so it will correctly print the length of the arbitrarily long input lines including as much of th possible text. */ #include<stdio.h> #define MAXLINE 1000 int getline(char [], int max); void copy(char from[], char to[]); int main() { int len = 0; /* current line length */ char line[MAXLINE]; /* current input line */ while((len = getline(line, MAXLINE)) > 0) { printf("LENGTH: %d\n", len); printf("LINE-CONTENTS: %s\n", line); } return 0; } int getline(char line[], int max) { int i = 0; /* simply an index counter */ int c = 0; /* variale to store user input */ for(i = 0; ((c = getchar()) != EOF) && c != '\n' && i < max - 1; ++i) line[i] = c; if(c == '\n') line[i++] = c; line[i] = '\0'; return i; }
a sample RUN
[arch@voodo kr2]$ gcc -ansi -pedantic -Wall -Wextra -O ex_1-16.c [arch@voodo kr2]$ ./a.out like this LENGTH: 10 LINE-CONTENTS: like this ooohhh... LENGTH: 10 LINE-CONTENTS: ooohhh... oue LENGTH: 4 LINE-CONTENTS: oue [arch@voodo kr2]$
Solution by Marnix
My solution was mostly similar to Richard Heathfields solution, but there was a minor bug in his version. The problem occurs when getline reads a line of exactly MAXLINE length, including the \n character. First I thought I was doing something wrong, so I added a printline function that prints the individual characters in the array with an index. The following example illustrates the problem with MAXLINE = 10 (each line ends with a newline):
marnix@trip:~/src/c/knr$ ./knr-1.16 abcdefgh a b c d e f g h \n \0 0 1 2 3 4 5 6 7 8 9 abcdefghi a b c d e f g h i \n \0 0 1 2 3 4 5 6 7 8 9 10 abcdefghij a b c d e f g h i \0 0 1 2 3 4 5 6 7 8 9
The bug was in the statement
if(i <= lim -1)
in the getline function, which caused the \n character to be placed in the last position of s[] when the input line was exactly MAXLINE. Now if the \0 character was written after that, this would cause the getline function to 'violate the array bounds' as Richard explained to me by e-mail. Changing the above statement to
if(i < lim -1)
fixed this bug. I will not post the complete solution, because it is mostly equal to Richards solution, but here is the printline function that I used to create the above output:
/* function prototype, place near the top of the code */ void printline(char line[]); /* printline: prints line upto and including the \0 character, with index */ void printline(char line[]) { int i, end, len; i = len = end = 0; printf("\n"); // print the characters while (end == 0) { if (line[i]=='\n') printf(" \\n "); else if (line[i]=='\t') printf(" \\t "); else if (line[i]=='\0') { printf(" \\0 "); end = 1; len = i; } else printf("%3c ",line[i]); ++i; } // print the index printf("\n"); for (i = 0; i<=len; ++i) { printf("%3d ",i); } printf("\n"); }
To use this function, just add the statement
printline(line);
as the first statement inside the while loop in main().
Solution by Balgo
I only changed the main routine. This version ignores the newlines like the unix command wc -L.
#include <stdio.h> #define MAXLINE 40 /* maximum input line size */ int getline(char line[], int maxline); void copy(char to[], char from[]); /* print longest input line */ int main() { int c; int len; /* current line length */ int max; /* maximum length seen so far */ char line[MAXLINE]; /* current input line */ char longest[MAXLINE]; /* longest line saved here */ max = 0; while ((len = getline(line, MAXLINE)) > 0) { if (line[len-1] != '\n') while ((c = getchar()) != EOF && c != '\n') ++len; if (len > max) { max = len; copy(longest, line); } } if (max > 0) { /* there was a line */ printf("Longest line with %d characters:\n", max); printf("%s ...\n", longest); } return 0; } /* getline: read a line s, return length */ int getline(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; } /* copy: copy 'from' into 'to'; assume to is big enough */ void copy(char to[], char from[]) { int i; i = 0; while ((to[i] = from[i]) != '\0') ++i; }
Solution by Btclcwiki
here, only change main
main() { int len; //current line length int max, overMax; //maximim length seen so far char line[MAXLINE]; //current input line char longest[MAXLINE]; //longest line saved here char overline[MAXLINE*MAXLINE]; //all characters over MAXLINE saved here max = 0; overMax = 0; while ((len = mygetline(line, MAXLINE)) > 0) { if (len > max) { max = len; copy(longest, line); } /**** * according mygetline * if length of current line greater than MAXLINE mygetline will set * line[MAXLINE-1] = '\0' and line[MAXLINE-2] != '\n' ****/ if (line[MAXLINE-2] != '\n') { int c, over; char buffer[MAXLINE*MAXLINE]; over = 0; while ((c = getchar()) != '\n') { buffer[over] = c; ++over; } buffer[over] = '\0'; if (over > overMax) { overMax = over; copy(longest, line); copy(overline, buffer); } } } if (max > 0) printf("%s", longest); if (overMax > 0) printf("%s", overline); return 0; }
test with MAXLINE = 10
123456789
111111111111111
22222222222222222222
333333333333333333333333
444444444444444444444444444444
ctril + d
444444444444444444444444444444
Solution by CakeOfTrust
Trying to figure out what "main routine" mentioned in the exercise means one will need to use both approaches to the problem:
1. Modifying main with some algorithm to keep track of the real length of a line. Here the property of remainders is used: a mod b will equal to c mod b if and only if (c - a) is some multiple of b (including 0). The while loop will only work when len mod (MAXLINE - 1) is 0, i.e. the line array is filled up to the end before '\0'.
#include <stdio.h> #define MAXLINE 2 #define NO 1 #define YES 0 int getlines(char *line, int maxline, char ch[]); void copyy(char to[], char from[]); int main(void) { int len = 0, max = len, templ = max; char line[MAXLINE], longest[MAXLINE], temp[MAXLINE], eofch[1]; eofch[0] = NO; while (eofch[0] == NO && (len = getlines(line, MAXLINE, eofch)) > 0) { if (len == (MAXLINE - 1) && line[MAXLINE - 2] != '\n') copyy(temp, line); while (eofch[0] == NO && (len - ((len / (MAXLINE - 1)) * (MAXLINE - 1))) == 0 && /* can be substituted with ((len * 1.0/ (MAXLINE * 1.0 - 1.0) - len / (MAXLINE - 1)) == 0.0) */ line[MAXLINE - 2] != '\n' && (templ = getlines(line, MAXLINE, eofch)) != 0) len += templ; if (len > max) { max = len; if (max <= (MAXLINE - 1) && (max < MAXLINE - 1 || line[MAXLINE - 2] == '\n')) copyy(longest, line); else copyy(longest, temp); } } if (max > 0) printf("\nThe longest line has length: %d\nLine: %s", max, longest); return 0; } int getlines(char s[], int lim, char checker[]) { 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'; if (c == EOF) checker[0] = YES; return i; } void copyy(char to[], char from[]) { int i; i = 0; while ((to[i] = from[i]) != '\0') ++i; }
2. Modifying the getline function:
#include <stdio.h> #define MAXLINE 10 #define NO 1 #define YES 0 int getlines(char *line, int maxline, char ch[]); void copyy(char to[], char from[]); int main(void) { int len = 0, max = len; char line[MAXLINE], longest[MAXLINE], eofch[1]; eofch[0] = NO; while (eofch[0] == NO && (len = getlines(line, MAXLINE, eofch)) > 0) if (len > max) { max = len; copyy(longest, line); } if (max > 0) printf("\nThe longest line has length: %d\nLine: %s", max , longest); return 0; } int getlines(char s[], int lim, char checker[]) { int i, c; for (i = 0; (c = getchar()) != EOF && c != '\n'; ++i) if (i < (lim - 1)) s[i] = c; if (c == '\n') { if (i < (lim - 1)) s[i] = '\n'; ++i; } if (i < lim - 1) s[i] = '\0'; else s[lim - 1] = '\0'; if (c == EOF) checker[0] = YES; return i; } void copyy(char to[], char from[]) { int i; i = 0; while ((to[i] = from[i]) != '\0') ++i; }
P.S. There were some problems during compilation because getline function has been defined already and it has been made to accept 3 arguments instead of 2. That is why in these 2 programs function getline is getlines while copy is copyy. It was made so to avoid calling functions from libraries.
Solution by Insight
Here is my version of revised main routine, using non-modified function getline (and with no use of getchar, as in previous examples).
main() { int textlen; /* text length */ int slen; /* full line length */ int max; /* maximum full line length */ int len; /* length returned by getline */ char line[MAXLINE]; int nonstop; /* flag to continue loop, instead of 'unknown' break statement */ textlen = 0; slen = 0; max = 0; nonstop = 1; while(nonstop) { len = getline(line, MAXLINE); slen = slen + len; if(len == 0) nonstop = 0; if(len == 0 || line[len-1] == '\n') { if(slen > max) max = slen; textlen = textlen + slen; slen = 0; } } if(max > 0) { printf("max line length: %d\n", max); printf("text length: %d\n", textlen); } return 0; }
Solution by Keyaedisa
UPDATE: I also completed the exercise the way the book seemingly wants. Only modify main function. Kept the exact same functionality and improved formatting. See below. Formatting of terminal logs is so that what you see is accurate to what it outputs. Both versions print line number, length, and full contents. The second version checks every box while also only modifying the main function. This hasn't been crazy tested.
FIRST ATTEMPT: Me just making it work. Do whatever it takes.
This is my solution based on how I interpreted the exercise. I wanted it too work first and look nice second. Too me I felt that I was being asked to modify the main function/action/ROUTINE of the program. So I did that by copying the code from the text and modifying for desired behavior. I realize now after finishing this exercise and looking for other examples to compare too that K&R likely meant just the main function. However this program by far produces the cleanest and most detailed output from any other solution I've seen. (This is my opinion). It also returns information (via standard output) in the same way the program from the book does: only after EOF and never before. As such I felt it appropriate too submit here. Be kind. It worked for me so I am posting here too help other beginners so they don't feel so bad about having as many lines as me. I could refine it but in the interest of progress I am posting here so that I can continue learning and hopefully it helps others. Please feel free to let me know how it could be improved. However, please do not replace as I believe it to be a different style of solution than most.
#include <stdio.h> #define MAXLINE 1000 /* maximum input line length */ /* excercise 1-16. revise the main routine of the longest-line program so it will correctly print the length of arbitrary long input lines, and as much as possible of the text. */ // replaced getLine with fetchLine int fetchLine(char line[], int maxline); void copy(char to[], char from[]); void addToStorage(char line[]); void printCurrentLine(int lineNumber); char lineStorage[MAXLINE][MAXLINE]; /* stores whole lines for later*/ int lineNumber = 0; int main(){ int len; /* current line length */ int max; /*maximum length seen so far */ int lineLengthStorage[MAXLINE]; /* stores lines lengths */ char line[MAXLINE]; /* current input line */ char longest[MAXLINE]; /* longest line saved here */ printf("Enter your text below to have it analyzed and returned to you.\n\n"); int i = 0; max = 0; while ((len = fetchLine(line, MAXLINE)) > 0){ addToStorage(line); ++lineNumber; lineLengthStorage[i] = len; ++i; if (len > max) { max = len; copy(longest, line); } } if (max > 0){ printf("\n"); printf("Longest line: %s", longest); for(int j = 0; j < MAXLINE && lineLengthStorage[j] != 0; ++j){ printf("Line number %d had a length of %d characters.\n", j+1, lineLengthStorage[j]-1); printCurrentLine(j); } } return 0; } /* fetchLine: read a line into s, return length */ int fetchLine(char s[],int lim){ int i; int 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; } /* copy: copy 'from' into 'to'; assume to is big enough */ void copy(char to[], char from[]){ int i; i = 0; while ((to[i] = from[i]) != '\0') ++i; } // addToStorage: adds LINE too lineStorage void addToStorage(char line[]){ int z = 0; while(line[z] != '\n'){ lineStorage[lineNumber][z] = line[z]; ++z; } ++z; lineStorage[lineNumber][z] = '\n'; } // printCurrentLine: prints line it was called on void printCurrentLine(int lineNumber){ printf("Line reads as follows: "); int z = 0; while(lineStorage[lineNumber][z] != '\n'){ printf("%c", lineStorage[lineNumber][z]); ++z; } printf("\n"); }
Example of output in terminal.
[key@xero KandR]$ gcc -Wall exc1-16.c -o exc1-16 [key@xero KandR]$ ./exc1-16 Enter your text below to have it analyzed and returned to you.<br><br> Hi my name is Keyaedisa.<br> This is so you may see an example of printed text.<br> I am very proud of this.<br><br> Longest line: This is so you may see an example of printed text.<br> Line number 1 had a length of 24 characters.<br> Line reads as follows: Hi my name is Keyaedisa.<br> Line number 2 had a length of 50 characters.<br> Line reads as follows: This is so you may see an example of printed text.<br> Line number 3 had a length of 24 characters.<br> Line reads as follows: I am very proud of this.<br> [key@xero KandR]$
SECOND ATTEMPT: Only modified the main function. As the book seemingly intended for this too be solved. getline() changed too getsline.
#include <stdio.h> #define MAXLINE 1000 /* maximum input line length */ /* couldn't let my self go on without doing it as ** the book asked. so here is the exact same functionality ** with better formatting */ int getsline(char line[], int maxline); void copy(char to[], char from[]); /* print the longest input line */ int main(){ int len; /* current line length */ int max; /* maximum length seen so far */ char line[MAXLINE]; /* current input line */ char longest[MAXLINE]; /* longest line saved here */ char lengths[MAXLINE]; /* keeps track of lengths */ char storedLines[MAXLINE][MAXLINE]; /* stores lines */ max = 0; int i = 0; /* lengths[] iterator */ int x = 0; /* storedLines[] iterator */ int lineNumber = 0; /* stores current line number */ printf("Enter your text below to have it analyzed and returned to you.\n\n"); while ((len = getsline(line, MAXLINE)) > 0){ lengths[i] = len; i++; int y = 0; while(line[y] != '\n'){ storedLines[lineNumber][y] = line[y]; ++y; } ++y; storedLines[lineNumber][y] = '\n'; ++lineNumber; if (len > max) { max = len; copy(longest, line); } } printf("\n"); if (max > 0){ /* there was a line */ printf("Longest line was: %s\n", longest); lineNumber = 0; /* lineNumber reset */ for(int j = 0; j < MAXLINE && lengths[j] > 0; ++j){ printf("Line %d has %d characters.\n", j + 1, lengths[j]-1); printf("It reads as follows: "); int z = 0; while(storedLines[lineNumber][z] != '\n'){ printf("%c", storedLines[lineNumber][z]); ++z; } ++lineNumber; printf("\n\n"); } } return 0; } /* getline: read a line into s, return length */ int getsline(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; } /* copy: copy 'from' into 'to'; assume to is big enough */ void copy(char to[], char from[]){ int i; i = 0; while ((to[i] = from[i]) != '\0') ++i; }
Below are two back to back samples in terminal. I believe that I have done this correctly and how the book wanted it.
[key@xero KandR]$ ./temp Enter your text below to have it analyzed and returned to you.<br> checking for desired functionality 123456789 12345678 1234567 123456 12345 1234 123 12 1 Longest line was: checking for desired functionality<br> Line 1 has 34 characters.<br> It reads as follows: checking for desired functionality<br> Line 2 has 9 characters.<br> It reads as follows: 123456789<br> Line 3 has 8 characters.<br> It reads as follows: 12345678<br> Line 4 has 7 characters.<br> It reads as follows: 1234567<br> Line 5 has 6 characters.<br> It reads as follows: 123456<br> Line 6 has 5 characters.<br> It reads as follows: 12345<br> Line 7 has 4 characters.<br> It reads as follows: 1234<br> Line 8 has 3 characters.<br> It reads as follows: 123<br> Line 9 has 2 characters.<br> It reads as follows: 12<br> Line 10 has 1 characters.<br> It reads as follows: 1<br> [key@xero KandR]$ ./temp Enter your text below to have it analyzed and returned to you.<br> did i finally fix it can i be as random as i want and even do this ok o and still be able to come back from it what happens if i end on a short one k Longest line was: and still be able to come back from it<br> Line 1 has 20 characters.<br> It reads as follows: did i finally fix it<br> Line 2 has 28 characters.<br> It reads as follows: can i be as random as i want<br> Line 3 has 16 characters.<br> It reads as follows: and even do this<br> Line 4 has 2 characters.<br> It reads as follows: ok<br> Line 5 has 1 characters.<br> It reads as follows: o<br> Line 6 has 38 characters.<br> It reads as follows: and still be able to come back from it<br> Line 7 has 36 characters.<br> It reads as follows: what happens if i end on a short one<br> Line 8 has 1 characters.<br> It reads as follows: k<br> [key@xero KandR]$