Why is this C program printing line longer than MAXLINE? - c

The program should print all the input lines which length is longer than MINLINE 5 and shorter than MAXLINE 10. Ref. K&R book exercise 1.17
#include <stdio.h>
#define MAXLINE 10
#define MINLINE 5
int getlines(char lines[], int maxline);
int main()
{
int length;
char lines[MAXLINE];
while ((length = getlines(lines, MAXLINE)) > 0)
{
if (length > MINLINE)
printf("%s", lines);
}
return 0;
}
int getlines(char lines[], int maxline)
{
int i, c;
for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
lines[i] = c;
}
if (c == '\n')
{
lines[i] = c;
++i;
}
lines[i] = '\0';
return i;
}
Desired outpur should be like this :-
Hello\n
Hello\n
hi\n
excuseMe\n
excuseMe\n
longLineNotToBePrinted\n
done
done
but unexpectedly the program printing lines that are far longer than MAXLINE and sometimes printing those omitting some trailing characters.

For starters this function
int getlines(char lines[], int maxline)
{
int i, c;
for (i = 0; i < maxline - 1 && (c = getchar()) != EOF && c != '\n'; ++i)
{
lines[i] = c;
}
if (c == '\n')
{
lines[i] = c;
++i;
}
lines[i] = '\0';
return i;
}
has undefined behavior because it can store the character '\0' at position maxline that is outside the array lines that has the valid range of indices [0, maxline).
As for your question about the output then if you entered a text that is greater than maxline then the function will return a string that does not contain the new line character '\n'. So the next string will be outputted in the same line.

/* Updated code. Now it is working fine.
Problems were both in main() function and in the function getlines().
#include <stdio.h>
#define MAXLINE 10
#define MINLINE 5
int getlines(char lines[], int maxline);
main()
{
int length;
char lines[MAXLINE];
while ((length = getlines(lines, MAXLINE)) > 0)
{
if (length > MINLINE)
{
/* As the input line can be longer than MAXLINE and in that case there will be no '\n' escape sequence to be stored in the lines[MAXLINE] array so we have used the if block to flow the control in such a way that when the input line is longer than MAXLINE, the output string will be printed manually with a '\n' *newline character. */
if (length > MAXLINE)
printf("%s\n", lines);
else
printf("%s", lines);
}
}
return 0;
}
int getlines(char lines[], int maxline)
{
int i, j, c;
i = 0;
for (j = 0; (c = getchar()) != EOF && c != '\n'; ++j)
{
/* In the for loop this time we didn't use the condition 'j < maxline
-1' as getchar() needs to read the whole input line no matter it's length(can be greater than MAXLINE), rather we have used the 'j < maxline -1' condition as a nested if block inside the for loop. While doing this to keep the getchar() function busy reaching the last input character no matter how long the line is we have used two variable i and j to overcome the problem in such a way that i will be used to store characters in the lines[MAXLINE] array, while j will be increased untill it reaches the end of the line. */
if (j < maxline - 1)
{
i = j;
lines[i] = c;
++i;
}
}
if (c == '\n')
{
if (j < maxline - 1)
{
lines[i] = c;
++i;
++j;
}
else
++j;
}
lines[i] = '\0';
return j;
}

Related

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;
}

A nearly lookalike strindex implementation of K&R found substring when there was no substring

I was trying to implement the K&R strindex program. User will be asked to enter a line, if the line contains the string "boi" in it, program will confirm that the line contained the pattern. The problem is, program confirms some other string/strings.
If i enter "şgb" it will confirm that it contains the string "boi". So far, it only happens with "şgb".
https://onlinegdb.com/SyeeO0mzH
#include <stdio.h>
#define MAXLINE_LENGTH 100
char pattern[] = "boi";
int get_line(char line[], int maxlength);
int str_index(char str[], char substr[]);
int main() {
char line[MAXLINE_LENGTH];
while(get_line(line, MAXLINE_LENGTH) > 0) {
if(str_index(line, pattern) >= 0) {
printf("%s", line);
printf("Pattern found above line\n");
}
}
return 0;
}
int get_line(char line[], int maxlength){
int index = 0, character;
while(--maxlength > 0 && (character = getchar()) != EOF && character != '\n') {
line[index++] = character;
}
if(character == '\n') {
line[index++] = character;
}
line[index] = '\0';
return index;
}
int str_index(char str[], char substr[]) {
int i, j, k;
for(i = 0; str[i] != '\0'; i++) {
for(j = i, k = 0; substr[k] != '\0' && str[j] == substr[k]; j++, k++) ;
if(k > 0) {
return i;
}
}
return -1;
}
boi
boi
Pattern found above line
fbajdobadşgbadf
fbajdobadşgbadf
Pattern found above line
şgb
şgb
Pattern found above line
In str_index, if any character in str is the first character in substr, then, when i is such that str[i] is that character, substr[j] == substr[k] will be true in the first iteration of for(j = i, k = 0;…, and k will be incremented. When that loop ends, k > 0 is true, and return i; will be executed.
You need to modify the code so that it returns i only if all the characters in substr have been matched.

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

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;
}

Resources